0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++でライブラリー(DLL)を作る Windows編

0
Last updated at Posted at 2025-11-21

C++でライブラリー(DLL)を作り、pythonなどから利用する方法について連載します。
  1. C++でライブラリーを作る Linux編
  2. pythonからC++のライブラリーを利用する Linux編
  3. C++でライブラリー(DLL)を作る Windows編
  4. pythonからC++のライブラリー(DLL)を利用する Windows編
  5. C#からC++のライブラリー(DLL)を利用する
  6. python・C#とC++間で複素数を受け渡す

このページでは、1.でLinux用に作ったライブラリーのWindows版(DLL)を紹介します。
使用環境はVisualStudio2022です。新しいプロジェクトの作成にて、「ダイナミックリンクライブラリー」(DLL)と「コンソールアプリ」(呼び出し側)で作成したプロジェクトを使用しました。

ライブラリー作成時のポイント

WindowsでDLL側も呼び出し側もC++の場合、DLL側は__declspec(dllexport)を、呼び出し側は__declspec(dllimport)をそれぞれ関数名の先頭に書いてインポートすれば動作します。

一方、Linux版と同様に、呼び出し側がpythonやC#の場合、エクスポート関数はCの書式である必要があり、DLL側は
  extern "C" __declspec(dllexport)
を関数名の先頭に書いてエクスポートします。

このおまじないは長ったらしいので、
  #define DLLAPI extern "C" __declspec(dllexport)
などと定義して一単語で済ませることが多いようです。

WindowsではDLLの開始時や終了時の処理をDllMain()内の DLL_PROCESS_ATTACH や DLL_PROCESS_DETACH で実施することが出来ます。

ライブラリーのサンプルコード

Linux版との相違は #include と上述のDLLAPIおよびDllMain()くらいです。pragmaはおせっかいなwarningを抑止するためのもので本題とは無関係です。コードの詳細につきましてはLinux版の説明をご覧下さい。

LocalLib.cpp
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define DLLAPI extern "C" __declspec(dllexport)
#include "LocalLib.h"

#pragma warning(disable:4996)

class CLocalClass {
public:
	CLocalClass(void);
	virtual ~CLocalClass(void);
	int Sum(int nInA, int nInB);
	int CalcX10(double *dpOut, double *dpIn, int nInLen);
	int TxRxMessage(char *bpOut, char *bpIn, int nInLen);
	int InOutStr(tagLibParameter *sp);
private:
};
CLocalClass *CP;


BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
	switch(ul_reason_for_call) {
		case DLL_PROCESS_ATTACH:
			CP = new CLocalClass;
			break;
		case DLL_PROCESS_DETACH:
			delete CP;
			break;
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		default:
			break;
	}
	return TRUE;
}


DLLAPI int LLibSum(int nInA, int nInB)
{
	int nRet = (CP->Sum)(nInA, nInB);
	return(nRet);
}


DLLAPI int LLibCalcX10(double *dpOut, double *dpIn, int nInLen)
{
	int nRet = (CP->CalcX10)(dpOut, dpIn, nInLen);
	return(nRet);
}


DLLAPI int LLibTxRxMessage(char *bpOut, char *bpIn, int nInLen)
{
	int nRet = (CP->TxRxMessage)(bpOut, bpIn, nInLen);
	return(nRet);
}


DLLAPI int LLibInOutStr(tagLibParameter *sp)
{
	int nRet = (CP->InOutStr)(sp);
	return(nRet);
}


//================================================================================
// CLocalClass
//================================================================================
CLocalClass::CLocalClass(void)
{
	printf("start DLL\n");
}


CLocalClass::~CLocalClass(void)
{
	printf("end DLL\n");
}


int CLocalClass::Sum(int nInA, int nInB)
{
	int nSum = nInA + nInB;
	return(nSum);
}


int CLocalClass::CalcX10(double *dpOut, double *dpIn, int nInLen)
{
	for(int nLoop=0; nLoop<nInLen; nLoop++) {
		(dpOut[nLoop]) = (dpIn[nLoop]) * 10.0;
	}
	return(0);
}


int CLocalClass::TxRxMessage(char *bpOut, char *bpIn, int nInLen)
{
	char sMsg[256];
	int nMsgLen, nCpLen;
	//--------
	sprintf(sMsg, "DLL->App: Hi ! rcvd[%s]", bpIn);
	nMsgLen = (int)strlen(sMsg);
	nCpLen = nMsgLen;
	if(nInLen<=nCpLen) {
		nCpLen = nInLen - 1;
	}
	memcpy(bpOut, sMsg, nCpLen);
	(bpOut[nCpLen]) = 0;
	return(nCpLen);
}


int CLocalClass::InOutStr(tagLibParameter *sp)
{
	struct timespec ts;
	struct tm *pTm;

	(sp->dOut) = (sp->dInA) + (sp->dInB);

	int nRet = timespec_get(&ts, TIME_UTC);
	pTm = localtime(&(ts.tv_sec));
	sprintf((sp->sData), "%04d %02d/%02d %02d:%02d:%02d %03d",
		((pTm->tm_year)+1900), ((pTm->tm_mon)+1), (pTm->tm_mday),
		(pTm->tm_hour), (pTm->tm_min), (pTm->tm_sec), (int)((ts.tv_nsec)/(1000*1000)));

	return(0);
}

ヘッダーファイルはDLL側でも呼び出し側でも両方使えるようなひと工夫(#ifndef〜#endif)を加えています。DLL側ではincludeする前に
  #define DLLAPI extern "C" __declspec(dllexport)
の一行を追加します。

LocalLib.h
#ifndef DLLAPI
#define DLLAPI extern "C" __declspec(dllimport)
#endif

typedef struct {
	double dInA;
	double dInB;
	double dOut;
	char sData[64];
} tagLibParameter;

DLLAPI int LLibSum(int nInA, int nInB);
DLLAPI int LLibCalcX10(double *dpOut, double *dpIn, int nInLen);
DLLAPI int LLibTxRxMessage(char *bpOut, char *bpIn, int nInLen);
DLLAPI int LLibInOutStr(tagLibParameter *sp);

呼び出し側のサンプルコード

Linux版との相違点は、文法自体はCなのですがファイル名は.cppでC++としてビルドしています。
ヘッダーファイルはDLLで作成したものと同じファイルです。上述のひと工夫により、こちらでは DLLAPI が
  extern "C" __declspec(dllimport)
になります。

DLLの使い方は、LoadLibrary()を使用する方法もあるのですが、本例ではインポートライブラリーを使用しています。
DLLのビルド時にDLLと共に生成される.libファイル(=インポートライブラリー)を呼び出し側のプロジェクトのソースファイルのディレクトリーへコピーして、ソースファイル内に
  #pragma comment(lib, "LocalLib.lib")
の一行を追加します。

コードの詳細につきましてはLinux版の説明をご覧下さい。
includeとpragma以外は同一内容です。

main.cpp
#include <stdio.h>
#include <string.h>
#include "LocalLib.h"

#pragma comment(lib, "LocalLib.lib")
#pragma warning(disable:4996)

int main()
{
	int nA, nB, nSum;
	nA = 1;
	nB = 2;
	//---------------------
	nSum = LLibSum(nA, nB);
	//---------------------
	printf("%d + %d = %d\n", nA, nB, nSum);
	//================================================================
	double dsOut[3], dsIn[3];
	int nLen = (sizeof(dsOut)) / (sizeof(double));
	dsIn[0] = 1.1;
	dsIn[1] = 2.2;
	dsIn[2] = 3.3;
	//----------------------------------------------------------
	LLibCalcX10(dsOut, dsIn, ((sizeof(dsIn))/(sizeof(double))));
	//----------------------------------------------------------
	printf("in[%.1lf %.1lf %.1lf]  out[%.1lf %.1lf %.1lf]\n",
			 (dsIn[0]),  (dsIn[1]),  (dsIn[2]),
			(dsOut[0]), (dsOut[1]), (dsOut[2]) );
	//================================================================
	char sTxBuf[64], sRxBuf[256];
	sprintf(sTxBuf, "App->DLL: Hello !");
	//----------------------------------------------
	LLibTxRxMessage(sRxBuf, sTxBuf, sizeof(sRxBuf));
	//----------------------------------------------
	printf("%s\n", sRxBuf);
	//================================================================
	tagLibParameter LP;
	memset(&LP, 0, sizeof(tagLibParameter));
	(LP.dInA) = 1.1;
	(LP.dInB) = 2.2;
	//----------------
	LLibInOutStr(&LP);
	//----------------
	printf("%.1lf + %.1lf = %.1lf  [%s]\n",
			(LP.dInA), (LP.dInB), (LP.dOut), (LP.sData));
}

実行例

呼び出し側のexeファイル(main.exe)とDLL(LocalLib.dll)を同じディレクトリーに置いて実行して下さい。
呼び出し側とDLLの間で、変数・配列・文字列・構造体のやりとりが双方向で出来ています。 Linux版と同じ結果です。

D:\>main.exe
start DLL
1 + 2 = 3
in[1.1 2.2 3.3]  out[11.0 22.0 33.0]
DLL->App: Hi ! rcvd[App->DLL: Hello !]
1.1 + 2.2 = 3.3  [2025 11/18 10:53:02 120]
end DLL
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?