トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

VC++のDLLを呼び出す_bcc

VC++のDLLを呼び出す

VC++で作成した共有ライブラリ(DLL)をBCCで呼び出します。この場合は、「VC++でのDLLはC言語の関数で定義」している必要があります。

たとえば、足し算・引き算を行う関数を持ったDLLを作ってみましょう。

VC++での作業(DLLの作成)

作成するDLL名を「AddTest.dll」とします。「AddTest.cpp」「AddTest.h」「main.cpp」「AddTest.def」というソースを作成します。

[AddTest.h]

#ifndef _ADDTEST_H
#define _ADDTEST_H

typedef struct {
    float x, y, z;
} S_VECTOR3;

#if __cplusplus
extern "C" {
#endif

int AddTest(int a, int b);
int SubTest(int a, int b);
S_VECTOR3 Vec3AddTest(S_VECTOR3 v1, S_VECTOR3 v2);

#if __cplusplus
}
#endif

#endif

[AddTest.cpp]

#include <stdio.h>
#include "AddTest.h"

int AddTest(int a, int b)
{
    return (a + b);
}

int SubTest(int a, int b)
{
    return (a - b);
}

S_VECTOR3 Vec3AddTest(S_VECTOR3 v1, S_VECTOR3 v2)
{
    S_VECTOR3 ret;

    ret.x = v1.x + v2.x;
    ret.y = v1.y + v2.y;
    ret.z = v1.z + v2.z;
    return ret;
}

[main.cpp]

#include <stdio.h>
#include <stdlib.h>

#include <windows.h>
#include <windowsx.h>

extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL,
        DWORD fdwReason, LPVOID lpvReserved)
{
    return TRUE;
}

[AddTest.def]

LIBRARY  AddTest
DESCRIPTION "AddTest - DLLのテスト用外部関数"

EXPORTS
    ; -- AddTest.cpp/h
    AddTest         @00001
    SubTest         @00002
    Vec3AddTest     @00003

このうち「main.cpp」はDllMainを定義しているだけのダミーです。実際は関数を呼び出すために使用しません。

関数定義は「AddTest.h」にて

extern "C" {
    ...
}

で囲っている点に注意してください。つまり、C言語の関数として明示的にしているわけです。なんで、C++のクラスやC++特有の命令は使用できません。(関数内では使用OKですが、入り口と出口部分はC言語で)

関数としては「AddTest」「SubTest」「Vec3AddTest」の3つです。「Vec3AddTest」は構造体の引数を渡して構造体を返しています。(アライメントに注意してください。VC++/BCC共にデフォルトは4バイトアライメントです)

「AddTest.def」は、外部関数として定義する関数を列挙します。1行目の「LIBRARY AddTest」にてDLL名を指定します。作成されるDLL名と同じ名称である必要があります。

「EXPORTS」以下でそれぞれ関数に対して一意な番号を割り振ってます。この関数と番号の対応付けにより、DLLの内部仕様が変わった場合でもDLLさえ入れ替えれば対応できるようになります。

これをコンパイルすると「AddTest.DLL」ができます。

BCCでの実行ファイルの作成

WindowsAPIの「LoadLibrary」でDLLを呼び出して、個々の関数を「GetProcAddress」にてマッピングします。DLL内の外部関数の記述がC言語書式であれば、BCCでもDelphiでもVBでも呼び出すことができるようになります。

ここではマッピング用のソース「AddTestIn.cpp」「AddTestIn.h」と、呼び出し側の「test.cpp」を作成しています。

[AddTestIn.h]

#ifndef _ADDTESTIN_H
#define _ADDTESTIN_H

typedef struct {
    float x, y, z;
} S_VECTOR3;

typedef int (* PADDTEST)(int, int);
typedef int (* PSUBTEST)(int, int);
typedef S_VECTOR3 (* PVEC3ADDTEST)(S_VECTOR3, S_VECTOR3);

extern PADDTEST g_pAddTest;
extern PSUBTEST g_pSubTest;
extern PVEC3ADDTEST g_pVec3AddTest;

int LoadDLL();
void FreeDLL();

#endif

[AddTestIn.cpp]

#include <stdio.h>
#include <windows.h>
#include <windowsx.h>
#include "AddTestIn.h"

static HINSTANCE g_DLLInst;
PADDTEST g_pAddTest;
PSUBTEST g_pSubTest;
PVEC3ADDTEST g_pVec3AddTest;

//----------------------------------------------//
//  DLLをマッピングする                         //
//----------------------------------------------//
int LoadDLL()
{
    g_DLLInst = LoadLibrary("AddTest.dll");
    if(!g_DLLInst) return 0;

    g_pAddTest     = (PADDTEST)GetProcAddress(g_DLLInst, "AddTest");
    g_pSubTest     = (PSUBTEST)GetProcAddress(g_DLLInst, "SubTest");
    g_pVec3AddTest = (PVEC3ADDTEST)GetProcAddress(g_DLLInst, "Vec3AddTest");
    
    return 1;
}

//----------------------------------------------//
//  DLLを開放する                               //
//----------------------------------------------//
void FreeDLL()
{
    FreeLibrary(g_DLLInst);
}

[test.cpp]

#include <stdio.h>
#include <stdlib.h>

#include "AddTestIn.h"

//----------------------------------------------//
//  メイン                                      //
//----------------------------------------------//
int main(int argc, char *argv[])
{
    int a, b;
    S_VECTOR3 v1, v2, v3;
    char szStr[256];
    
    //DLLの呼び出し
    if(!LoadDLL()) {
        printf("AddTest.dllの読み込みに失敗しました。\n");
        return -1;
    }
    
    printf("DLLより足し算関数を呼び出します。\n");
    a = 125;
    b = 51;
    sprintf(szStr, "%d + %d = %d\n", a, b, g_pAddTest(a, b));
    printf(szStr);
    sprintf(szStr, "%d - %d = %d\n", a, b, g_pSubTest(a, b));
    printf(szStr);

    v1.x = 1.4f;
    v1.y = 2.5f;
    v1.z = 3.1f;
    v2.x = 4.2f;
    v2.y = 6.6f;
    v2.z = 9.3f;
    v3 = g_pVec3AddTest(v1, v2);
    
    sprintf(szStr, "(%.2f, %.2f, %.2f) + (%.2f, %.2f, %.2f) = (%.2f, %.2f, %.2f)\n",
        v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z);
    printf(szStr);

    //DLLの開放
    FreeDLL();
    
    return 0;
}

「typedef int (* PADDTEST)(int, int);」の記述にて関数を呼び出すときの型を宣言しています。この場合は型名が「PADDTEST」で引数は2つのint、戻り値がintです。

PADDTEST g_pAddTest;

で「関数のポインタ」を実体化しています。次に「LoadLibrary」でDLLを呼び出したときのハンドルを元に

g_pAddTest = (PADDTEST)GetProcAddress(g_DLLInst, "AddTest");

として、関数「AddTest」を「AddTest.dll」より探し出して「g_pAddTest」にマッピングしています。以降は「c = g_pAddTest(a, b);」として普通の関数のように呼び出すことができます。なお、DLLでの関数名や引数の方はこの呼び出し側での定義と同じである必要があります。関数が存在しない場合・引数が間違っている場合はNULLが返されます。

「AddTestIn.h」にてこれらの関数(のポインタ)を「extern」で宣言していますが、これは他のファイルで共有して使うためです。実体は「AddTestIn.cpp」の先頭で定義されています。

最後に、これらをコンパイルするためのmakefile「makefile.mak」を作成します。

[makefile.mak]

CC      = bcc32
LINKER  = ilink32

INCLUDE = -I"i:\User\bcc program"
LIB     = -L"h:\program files\Borland\Bcc55\lib;i:\User\bcc program"


#コンソールアプリケーションのためのコンパイルオプション
CFLAGS = -O2 -w -tWC
LFLAGS = /Tpe

TARGET = test.exe
OBJS   = test.obj AddTestIn.obj

ALL : $(TARGET)

# -- objファイルの作成
AddTestIn.obj: AddTestIn.cpp AddTestIn.h
 $(CC) $(CFLAGS) -c AddTestIn.cpp

test.obj: test.cpp
 $(CC) $(CFLAGS) -c test.cpp

# -- exeファイルの作成
$(TARGET): $(OBJS)
 $(LINKER) $(LFLAGS) $(LIB) $(OBJS) c0x32.obj,$(TARGET),,cw32.lib import32.lib

clean:
 del *.obj
 del *.tds
 del *.il*
 del *.map

メイク処理は

make -f makefile.mak

で成功すると「test.exe」が生成されます。VC++で作成した「AddTest.dll」を同じディレクトリに置いてコンソールから実行すると、以下のように出力されます。

DLLより足し算関数を呼び出します。
125 + 51 = 176
125 - 51 = 74
(1.40, 2.50, 3.10) + (4.20, 6.60, 9.30) = (5.60, 9.10, 12.40)

プロジェクトとソース

上記サンプルのVC++(6.0)のプロジェクトとソースは以下になります。

vc_AddTest_dll_20040905.lzh(241)

BCCのソースとmakefileは以下になります。

bcc_AddTest_20040905.lzh(231)

DLLの外部関数を確認するには

Dependency Walker」というDLL/EXEの内部構造を見るツールが便利です。これを一回実行すると、DLLをエクスプローラで選択した状態のポップアップメニューに「View Dependencies...」というのが追加されます。これを選択するとDLLの内部構造が表示されます。

「AddTest」「SubTest」「Vec3AddTest」がC言語の関数として定義されているのが分かりますね。「Ordinal」の列がdefファイルでの番号になります。また、DLLの依存関係の確認やうっかりデバッグでビルドしてしまった、とかいうのもこの「Dependency Walker」で確認することができます。

Future's Laboratory 技術格納庫 2004-2013 Yutaka Yoshisaka.