2020年7月アーカイブ

前々回の「VBフォーム(Windows アプリケーション)だって、コマンドプロンプトでDLL?」記事で、"VBフォームからDLLを呼び出す場合、上記エイリアスの手法を .def ファイル内で使用する必要があるらしい"と書きましたが、ちょっと違っていたようです。

マイクロソフトのドキュメントに、

Visual Basic で書かれたプログラムから DLL を呼び出す場合は、ここで示したエイリアスの手法を .def ファイル内で使用する必要があります。エイリアスが Visual Basic プログラムの中で宣言されている場合は、.def ファイル内でのエイリアスは不要です。これは、Visual Basic プログラムでは、Declare ステートメントにエイリアス句を追加することで実現されます。

とあったのですが、VBフォームでエイリアス句の動作確認をしようとして気が付きました。

訂正で、VBフォーム ⇒ CのDLL、VBフォーム ⇒ C++のDLLを呼び出すだけのサンプルを作ってみました。

<注意点>

・DLLでエクスポートしたい関数などは、.def ファイルに記述する必要があります。

・.def ファイルでも、VBフォームでも、エイリアスは一切必要無さそうです。

・DLLファイルが無かったり、呼び出す関数がエクスポートされていない(.def ファイルに記述が無い)と、VB側で例外が発生します。

・お試し結果の出力は、VBフォームの実行ファイル(.exe)をDOS窓でリダイレクト(> や >>)して得ています。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ & Visual Basic 2008


/*---- VB Form ⇒ C の dll 作成 --------------- コマンドライン -------------*/

D:\vc2008\dllchk>cl /LD testc.c /link /def:testc.def
(testc.dll を作成)

/*----------------------------------------------------------------------------*/

/*---- VB Form ⇒ C++ の dll 作成 ------------- コマンドライン -------------*/

D:\vc2008\dllchk>cl /LD testpp.cpp /link /def:testpp.def
(testpp.dll を作成)

/*----------------------------------------------------------------------------*/


/*---- VB Form ⇒ C の dll 呼出し ------------ お試し結果 ------------------*/

CPRINT01 : String from Visual Basic : Visual Basic01 です。 - 17
CPRINT02 : Values from Visual Basic : 200

VBPRINT01 Return C String : Hi! Visual Basic
VBPRINT02 Called C : Return from C
VBPRINT02 Return C Values : 40000
EXCEPTION DLL 'testc.dll' の 'subc05' というエントリ ポイントが見つかりません。

/*----------------------------------------------------------------------------*/

/*---- VB Form ⇒ C++ の dll 呼出し ------------- お試し結果 ---------------*/

C++PRINT03 : String from Visual Basic : Visual Basic03 です。 - 19
C++PRINT04 : Values from Visual Basic : 300

VBPRINT03 Called C++ : Return from Visual C++
VBPRINT03 Return C++ String : Hello Visual Basic
VBPRINT04 Return C++ Values : 90000
EXCEPTION DLL 'testpp.dll' の 'subpp06' というエントリ ポイントが見つかりません。

/*----------------------------------------------------------------------------*/

/*---- .def 無しで dll 作成 と .dll 無しでの呼出し ----- 結果 --------------*/

EXCEPTION DLL 'testc.dll' の 'subc01' というエントリ ポイントが見つかりません。

EXCEPTION DLL 'testpp.dll' を読み込めません: 指定されたモジュールが見つかりません。(HRESULT からの例外: 0x8007007E)

/*----------------------------------------------------------------------------*/


/*==== VB Form ⇒ C の dll 呼出し ============= お試しソース ===============*/
/*---- testc.def -------------------------------------------------------------*/

LIBRARY testc
EXPORTS
 subc01
 subc02

/*----------------------------------------------------------------------------*/

/*---- testc.c ---------------------------------------------------------------*/

#include <stdio.h>
#include <string.h>   // strcpy_s 関数

void __stdcall subc01(char *s, int d)
{
    printf("CPRINT01 : String from Visual Basic : %s - %d\n", s, d);
    strcpy_s(s, d, "Hi! Visual Basic");
}

char * __stdcall subc02(int *d)
{
    printf("CPRINT02 : Values from Visual Basic : %d\n", *d);
    *d *= *d;
    return("Return from C");
}

void __stdcall subc05(void)
{
    printf("CPRINT05 : Called C ?\n");
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/


/*==== VB Form ⇒ C++ の dll 呼出し ============= お試しソース =============*/
/*---- testpp.def ------------------------------------------------------------*/

LIBRARY testpp
EXPORTS
 subpp03
 subpp04

/*----------------------------------------------------------------------------*/

/*---- testpp.cpp ------------------------------------------------------------*/

#include <cstdio>
#include <cstring>   // strcpy_s 関数

extern "C" char * __stdcall subpp03(char *s, int d)
{
    printf("C++PRINT03 : String from Visual Basic : %s - %d\n", s, d);
    strcpy_s(s, (unsigned int)d, "Hello Visual Basic");
    return("Return from Visual C++");
}

extern "C" void __stdcall subpp04(int *d)
{
    printf("C++PRINT04 : Values from Visual Basic : %d\n", *d);
    *d *= *d;
}

extern "C" void __stdcall subpp06(void)
{
    printf("C++PRINT06 : Called Visual C++ ?\n");
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/


/*==== VB Form ================================ お試しソース ===============*/
/*----------------------------------------------------------------------------*/

Public Class Form1

    Private Declare Sub subc01 Lib "testc.dll" (ByVal s As String, ByVal d As Integer)
    Private Declare Function subc02 Lib "testc.dll" (ByRef d As Integer) As String
    Private Declare Sub subc05 Lib "testc.dll" ()

    Private Declare Function subpp03 Lib "testpp.dll" (ByVal s As String, ByVal d As Integer) As String
    Private Declare Sub subpp04 Lib "testpp.dll" (ByRef d As Integer)
    Private Declare Sub subpp06 Lib "testpp.dll" ()

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim a As Integer = 17
        Dim d As Integer = 200
        Dim s As String = "Visual Basic01 です。"

        Try
            subc01(s, a)
            Console.WriteLine("VBPRINT01 Return C String : " & Strings.Left(s, InStr(s, Chr(0)) - 1))
            Console.WriteLine("VBPRINT02 Called C : " & subc02(d))
            Console.WriteLine("VBPRINT02 Return C Values : " & d)
            subc05()
        Catch ex As EntryPointNotFoundException
            Console.WriteLine("EXCEPTION " & Err.Description)
        Catch ex As DllNotFoundException
            Console.WriteLine("EXCEPTION " & Err.Description)
        Catch ex As Exception
            Console.WriteLine("EXCEPTION その他の例外が発生しました。")
        End Try
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim a As Integer = 19
        Dim d As Integer = 300
        Dim s As String = "Visual Basic03 です。"

        Try
            Console.WriteLine("VBPRINT03 Called C++ : " & subpp03(s, a))
            Console.WriteLine("VBPRINT03 Return C++ String : " & s)
            subpp04(d)
            Console.WriteLine("VBPRINT04 Return C++ Values : " & d)
            subpp06()
        Catch ex As EntryPointNotFoundException
            Console.WriteLine("EXCEPTION " & Err.Description)
        Catch ex As DllNotFoundException
            Console.WriteLine("EXCEPTION " & Err.Description)
        Catch ex As Exception
            Console.WriteLine("EXCEPTION その他の例外が発生しました。")
        End Try
    End Sub
End Class

/*----------------------------------------------------------------------------*/
/*============================================================================*/

DLLには、プロセスの起動時に読込む静的リンクと、プログラム処理中に動的に読込みと解放を行う明示的リンクが有ります。

ずぼらでめんどくさがり屋には、なるべく簡単な方が宜しいかと。

試しに、C++ ⇒ CのDLL、C ⇒ C++のDLLを動的に呼び出すだけのサンプルを作ってみました。

<注意点>

・C++の文字列(char *)は、デフォルトで定数設定がされているようで、C側で変更するとエラーが発生します。止むを得ず、配列データ(char [])としてC側に渡します。

・VBフォームからの呼出しでも気になっていたのですが、atoi() 関数が上手く機能していないような・・・。(手抜きかよ?)原因は不明です。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- C++ ⇒ C の dll 作成 ---------- コマンドライン ---------------------*/

D:\vc2008\dllchk>cl /LD testc.c /link /def:testc.def
(testc.dll を作成)

D:\vc2008\dllchk>cl call_cdll.cpp

/*----------------------------------------------------------------------------*/

/*---- C ⇒ C++ の dll 作成 ---------- コマンドライン ----------------------*/

D:\vc2008\dllchk>cl /LD testpp.cpp /link /def:testpp.def
(testpp.dll を作成)

D:\vc2008\dllchk>cl call_cppdll.c

/*----------------------------------------------------------------------------*/


/*---- C++ ⇒ C の dll 呼出し -------- お試し結果 --------------------------*/

D:\vc2008\dllchk>call_cdll
CPRINT : String from C++ : Visual C++ からの呼び出しです。 - 15
C++PRINT : subc - 0
Cのメッセージ : Hi! Visual C++

/*----------------------------------------------------------------------------*/

/*---- C ⇒ C++ の dll 呼出し -------- お試し結果 --------------------------*/

D:\vc2008\dllchk>call_cppdll
C++PRINT : String from C : Visual C からの呼び出しです。 - 15
CPRINT : subpp - 0
C++のメッセージ : Hello Visual C

/*----------------------------------------------------------------------------*/


/*==== C++ ⇒ C の dll 呼出し ======== お試しソース ========================*/
/*---- testc.def -------------------------------------------------------------*/

LIBRARY testc
EXPORTS
 subc

/*----------------------------------------------------------------------------*/

/*---- testc.c ---------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>   // atoi 関数
#include <string.h>   // strcpy_s 関数

int subc(char *s, int d)
{
    printf("CPRINT : String from C++ : %s - %d\n", s, d);
    strcpy_s(s, d, "Hi! Visual C++");
    return(atoi(s) * d);
}

/*----------------------------------------------------------------------------*/

/*---- call_cdll.cpp ---------------------------------------------------------*/

#include <cstdio>
#include <windows.h>

void main(void)
{
    HINSTANCE hDll;
    int (*func)(char *, int);
    char s[] = "Visual C++ からの呼び出しです。";
//  char *s = "Visual C++ からの呼び出しです。";

    if((hDll = LoadLibrary("testc.dll")) != NULL) {
        if((func = (int (*)(char *, int))GetProcAddress(hDll, "subc")) != NULL) {
            printf("C++PRINT : subc - %d\n", func(s, 15));
            printf("Cのメッセージ : %s\n", s);
            FreeLibrary(hDll);
        } else {
            printf("GetProcAddress 関数の呼び出しに失敗しました。\n");
            FreeLibrary(hDll);
        }
    } else {
        printf("LoadLibrary 関数の呼び出しに失敗しました。\n");
    }
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/


/*==== C ⇒ C++ の dll 呼出し ======== お試しソース ========================*/
/*---- testpp.def ------------------------------------------------------------*/

LIBRARY testpp
EXPORTS
 subpp

/*----------------------------------------------------------------------------*/

/*---- testpp.cpp ------------------------------------------------------------*/

#include <cstdio>
#include <cstdlib>   // atoi 関数
#include <cstring>   // strcpy_s 関数

extern "C" int subpp(char *s, int d)
{
    printf("C++PRINT : String from C : %s - %d\n", s, d);
    strcpy_s(s, (unsigned int)d, "Hello Visual C");
    return(atoi(s) * d);
}

/*----------------------------------------------------------------------------*/

/*---- call_cppdll.c ---------------------------------------------------------*/

#include <stdio.h>
#include <windows.h>

void main(void)
{
    HINSTANCE hDll;
    int (*func)(char *, int);
    char *s = "Visual C からの呼び出しです。";

    if((hDll = LoadLibrary("testpp.dll")) != NULL) {
        if((func = (int (*)(char *, int))GetProcAddress(hDll, "subpp")) != NULL) {
            printf("CPRINT : subpp - %d\n", func(s, 15));
            printf("C++のメッセージ : %s\n", s);
            FreeLibrary(hDll);
        } else {
            printf("GetProcAddress 関数の呼び出しに失敗しました。\n");
            FreeLibrary(hDll);
        }
    } else {
        printf("LoadLibrary 関数の呼び出しに失敗しました。\n");
    }
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

ずぼらでめんどくさがり屋には、Windows アプリケーションの真似事をするなら、VBフォームが打って付け。

試しに、VBフォーム ⇒ CのDLL、VBフォーム ⇒ C++のDLLを呼び出すだけのサンプルを作ってみました。

<注意点>

・VBフォームに限らず、Windows アプリケーションから呼び出すなら、関数などの前に「__stdcall」の呼出し規則を付ける事はお約束! *1 *2

・.def ファイルで、CのDLLの場合「subc01 = _subc01@4」、C++のDLLの場合「subpp01 = _subpp01@0」の様にエイリアス指定すると、VBフォームから呼び出す手間が、ちょっとだけ省けます。*3

・dumpbin のオプション /symbols や /exports を使って、SYMBOLS 名や EXPORTS 名を調べることが出来ます。*4

・VBの文字列(String)には、終端文字(Cで文字列の終わりを示す '\0')が無いようです。

・Cの関数などで文字列(char *)操作した結果をVB側で表示すると不具合が生じるため、「Strings.Left(s, InStr(s, Chr(0)) - 1)」で終端文字を除外します。

・C++の文字列(char *)には、終端文字が無いのか不明です。

・お試し結果の出力は、VBフォームの実行ファイル(.exe)をDOS窓でリダイレクト(> や >>)して得ています。


*1:VBフォームから C/C++ DLL 内の関数を呼び出す場合、正しい呼び出し規則を使ってコンパイラによる名前装飾に影響されずに、エクスポートされる必要があるらしい。

*2:__stdcall による関数呼び出し規則は、呼び出された関数がスタックをクリアし、パラメーターは右から左へ渡され、関数名は異なる方法で装飾されるらしい。

*3:__stdcall による装飾を外すには、.def ファイルの EXPORTS セクションにエイリアスを使って、その名前を指定する必要があるらしい。(つまり、VBフォームからDLLを呼び出す場合、上記エイリアスの手法を .def ファイル内で使用する必要があるらしい)

*4:__stdcall で装飾された名前には、シンボル名の前にアンダースコア (_) が付けられ、シンボルの後にはアットマーク (@) が付けられ、その後に引数リストのバイト数 (必要なスタックサイズ) が付けられるらしい。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ & Visual Basic 2008


/*---- VB Form ⇒ C の dll 作成 --------------- コマンドライン -------------*/

D:\vc2008\dllchk>cl /LD testc.c /link /def:testc.def
(testc.dll を作成)

/*----------------------------------------------------------------------------*/

/*---- VB Form ⇒ C++ の dll 作成 ------------- コマンドライン -------------*/

D:\vc2008\dllchk>cl /LD testpp.cpp /link /def:testpp.def
(testpp.dll を作成)

/*----------------------------------------------------------------------------*/


/*---- VB Form ⇒ C の dll 呼出し ------------ お試し結果 ------------------*/

CPRINT01 : Value from Visual Basic : 100
CPRINT02 : Value from Visual Basic : 200
CPRINT03 : Cのファンクションが呼ばれました
CPRINT04 : Cのサブルーチンが呼ばれました
CPRINT05 : String from Visual Basic : Visual Basic05 です。 - 17
CPRINT06 : Values from Visual Basic : [ , ]

VBPRINT01 Called C : 10000
VBPRINT02 Return C Value : 777
VBPRINT03 Called C : 12345
VBPRINT05 Called C : 0
VBPRINT05 Return C String : Hi! Visual Basic
VBPRINT06 Called C : Return from C

/*----------------------------------------------------------------------------*/

/*---- VB Form ⇒ C++ の dll 呼出し ------------- お試し結果 ---------------*/

C++PRINT01 : C++のサブルーチンが呼ばれました
C++PRINT02 : Values from Visual Basic : 10, 100
C++PRINT03 : String from Visual Basic : Visual Basic03 です。 - 19
C++PRINT04 : Values from Visual Basic : 1.100000, 2.200000
C++PRINT05 : Values from Visual Basic : OK!
C++PRINT06 : C++のファンクションが呼ばれました

VBPRINT02 Called C++ : 1000
VBPRINT03 Return C++ String : Hello Visual Basic
VBPRINT04 Called C++ : 3.3
VBPRINT05 Called C++ : Return from Visual C++
VBPRINT05 Return C++ Values : NG!
VBPRINT06 Called C++ : 24680

/*----------------------------------------------------------------------------*/


/*==== VB Form ⇒ C の dll 呼出し ============= お試しソース ===============*/
/*---- testc.def -------------------------------------------------------------*/

LIBRARY testc
EXPORTS
 subc01 = _subc01@4
 subc02 = _subc02@4
 subc03 = _subc03@0
 subc04 = _subc04@0
 subc05 = _subc05@8
 subc06 = _subc06@8

/*----------------------------------------------------------------------------*/

/*---- testc.c ---------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>   // atoi 関数
#include <string.h>   // strcpy_s 関数

int __stdcall subc01(int d)
{
    printf("CPRINT01 : Value from Visual Basic : %d\n", d);
    return(d * 100);
}

void __stdcall subc02(int *d)
{
    printf("CPRINT02 : Value from Visual Basic : %d\n", *d);
    *d = 777;
}

int __stdcall subc03(void)
{
    printf("CPRINT03 : Cのファンクションが呼ばれました\n");
    return(12345);
}

void __stdcall subc04(void)
{
    printf("CPRINT04 : Cのサブルーチンが呼ばれました\n");
}

int __stdcall subc05(char *s, int d)
{
    printf("CPRINT05 : String from Visual Basic : %s - %d\n", s, d);
    strcpy_s(s, d, "Hi! Visual Basic");
    return(atoi(s) * d);
}

char * __stdcall subc06(char c, char d)
{
    printf("CPRINT06 : Values from Visual Basic : %c , %c\n", c, d);
    return("Return from C");
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/


/*==== VB Form ⇒ C++ の dll 呼出し ============= お試しソース =============*/
/*---- testpp.def ------------------------------------------------------------*/

LIBRARY testpp
EXPORTS
 subpp01 = _subpp01@0
 subpp02 = _subpp02@8
 subpp03 = _subpp03@8
 subpp04 = _subpp04@16
 subpp05 = _subpp05@12
 subpp06 = _subpp06@0

/*----------------------------------------------------------------------------*/

/*---- testpp.cpp ------------------------------------------------------------*/

#include <cstdio>
#include <cstring>   // strcpy_s 関数

extern "C" void __stdcall subpp01(void)
{
    printf("C++PRINT01 : C++のサブルーチンが呼ばれました\n");
}

extern "C" int __stdcall subpp02(int a, int b)
{
    printf("C++PRINT02 : Values from Visual Basic : %d, %d\n", a, b);
    return(a * b);
}

extern "C" void __stdcall subpp03(char *s, int d)
{
    printf("C++PRINT03 : String from Visual Basic : %s - %d\n", s, d);
    strcpy_s(s, (unsigned int)d, "Hello Visual Basic");
}

extern "C" double __stdcall subpp04(double a, double b)
{
    printf("C++PRINT04 : Values from Visual Basic : %f, %f\n", a, b);
    return(a + b);
}

extern "C" char * __stdcall subpp05(char *a, char *b, char c)
{
    printf("C++PRINT05 : Values from Visual Basic : %c%c%c\n", *a, *b, c);
    *a = 'N', *b = 'G';
    return("Return from Visual C++");
}

extern "C" int __stdcall subpp06(void)
{
    printf("C++PRINT06 : C++のファンクションが呼ばれました\n");
    return(24680);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/


/*==== VB Form ================================ お試しソース ===============*/
/*----------------------------------------------------------------------------*/

Public Class Form1

    Private Declare Function subc01 Lib "testc.dll" (ByVal d As Integer) As Integer
    Private Declare Sub subc02 Lib "testc.dll" (ByRef d As Integer)
    Private Declare Function subc03 Lib "testc.dll" () As Integer
    Private Declare Sub subc04 Lib "testc.dll" ()
    Private Declare Function subc05 Lib "testc.dll" (ByVal s As String, ByVal d As Integer) As Integer
    Private Declare Function subc06 Lib "testc.dll" (ByVal b As SByte, ByVal c As SByte) As String

    Private Declare Sub subpp01 Lib "testpp.dll" ()
    Private Declare Function subpp02 Lib "testpp.dll" (ByVal a As Integer, ByVal d As Integer) As Integer
    Private Declare Sub subpp03 Lib "testpp.dll" (ByVal s As String, ByVal d As Integer)
    Private Declare Function subpp04 Lib "testpp.dll" (ByVal i As Double, ByVal j As Double) As Double
    Private Declare Function subpp05 Lib "testpp.dll" (ByRef l As SByte, ByRef m As SByte, ByVal n As SByte) As String
    Private Declare Function subpp06 Lib "testpp.dll" () As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim a As Integer = 17
        Dim b As SByte = 91
        Dim c As SByte = 93
        Dim d As Integer = 100
        Dim i As Integer = 200
        Dim s As String = "Visual Basic05 です。"

        Console.WriteLine("VBPRINT01 Called C : " & subc01(d))
        subc02(i)
        Console.WriteLine("VBPRINT02 Return C Value : " & i)
        Console.WriteLine("VBPRINT03 Called C : " & subc03())
        subc04()
        Console.WriteLine("VBPRINT05 Called C : " & subc05(s, a))
        Console.WriteLine("VBPRINT05 Return C String : " & Strings.Left(s, InStr(s, Chr(0)) - 1))
        Console.WriteLine("VBPRINT06 Called C : " & subc06(b, c))
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim a As Integer = 10
        Dim b As Integer = 100
        Dim d As Integer = 19
        Dim s As String = "Visual Basic03 です。"
        Dim i As Double = 1.1
        Dim j As Double = 2.2
        Dim l As SByte = 79
        Dim m As SByte = 75
        Dim n As SByte = 33

        subpp01()
        Console.WriteLine("VBPRINT02 Called C++ : " & subpp02(a, b))
        subpp03(s, d)
        Console.WriteLine("VBPRINT03 Return C++ String : " & s)
        Console.WriteLine("VBPRINT04 Called C++ : " & subpp04(i, j))
        Console.WriteLine("VBPRINT05 Called C++ : " & subpp05(l, m, n))
        Console.WriteLine("VBPRINT05 Return C++ Values : " & Chr(l) & Chr(m) & Chr(n))
        Console.WriteLine("VBPRINT06 Called C++ : " & subpp06())
    End Sub
End Class

/*----------------------------------------------------------------------------*/
/*============================================================================*/

コードの使い回しや部品(サブルーチンや関数)の再利用、ライブラリー化、構造化などは、C++が騒ぎ出す遥か以前からC言語で導入済みでした。

今、C++ってどうなの? 影が薄くなったって言うか風前の灯、一歩手前?
他の簡潔明瞭な言語に取って代わられつつあり、C++其の物が孤立無援というか、ガラパゴス化(複雑怪奇)感満載というか・・・。

それはさて置き。

ずぼらのめんどくさがり屋は、IDEなんて使いこなせる訳が有りません。DOS窓(コマンドプロンプト)にcl入れて、コンパイルするのが関の山。

試しに、C++ ⇒ CのDLL、C ⇒ C++のDLLを呼び出すだけのサンプルを作ってみました。

お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- C++ ⇒ C の dll 作成 ---------- コマンドライン ---------------------*/

D:\vc2008\dllchk>cl /LD testc.c /link /def:testc.def
(ライブラリ testc.lib も dll と一緒に作成されます)

D:\vc2008\dllchk>cl testpp.cpp testc.lib

/*----------------------------------------------------------------------------*/

/*---- C ⇒ C++ の dll 作成 ---------- コマンドライン ----------------------*/

D:\vc2008\dllchk>cl /LD testpp.cpp /link /def:testpp.def
(ライブラリ testpp.lib も dll と一緒に作成されます)

D:\vc2008\dllchk>cl testc.c testpp.lib

/*----------------------------------------------------------------------------*/


/*---- C++ ⇒ C の dll 呼出し -------- お試し結果 --------------------------*/

D:\vc2008\dllchk>testpp
Cのサブルーチン1が呼ばれました
Cのサブルーチン2が呼ばれました
Cのサブルーチン3が呼ばれました

/*----------------------------------------------------------------------------*/

/*---- C ⇒ C++ の dll 呼出し -------- お試し結果 --------------------------*/

D:\vc2008\dllchk>testc
C++のサブルーチン1が呼ばれました
C++のサブルーチン2が呼ばれました
C++のサブルーチン3が呼ばれました

/*----------------------------------------------------------------------------*/


/*==== C++ ⇒ C の dll 呼出し ======== お試しソース ========================*/
/*---- testc.def -------------------------------------------------------------*/

LIBRARY testc
EXPORTS
 subc1
 subc2
 subc3

/*----------------------------------------------------------------------------*/

/*---- testc.c ---------------------------------------------------------------*/

#include <stdio.h>

void subc1(void)
{
    printf("Cのサブルーチン1が呼ばれました\n");
}

void subc2(void)
{
    printf("Cのサブルーチン2が呼ばれました\n");
}

void subc3(void)
{
    printf("Cのサブルーチン3が呼ばれました\n");
}

/*----------------------------------------------------------------------------*/

/*---- testpp.cpp ------------------------------------------------------------*/

#include <cstdio>

extern "C" void subc1(void);
extern "C" void subc2(void);
extern "C" void subc3(void);

void main(void)
{
    subc1();
    subc2();
    subc3();
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/


/*==== C ⇒ C++ の dll 呼出し ======== お試しソース ========================*/
/*---- testpp.def ------------------------------------------------------------*/

LIBRARY testpp
EXPORTS
 subpp1
 subpp2
 subpp3

/*----------------------------------------------------------------------------*/

/*---- testc.c ---------------------------------------------------------------*/

#include <stdio.h>

extern void subpp1(void);
extern void subpp2(void);
extern void subpp3(void);

void main(void)
{
    subpp1();
    subpp2();
    subpp3();
}

/*----------------------------------------------------------------------------*/

/*---- testpp.cpp ------------------------------------------------------------*/

#include <cstdio>

extern "C" void subpp1(void)
{
    printf("C++のサブルーチン1が呼ばれました\n");
}

extern "C" void subpp2(void)
{
    printf("C++のサブルーチン2が呼ばれました\n");
}

extern "C" void subpp3(void)
{
    printf("C++のサブルーチン3が呼ばれました\n");
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

むかし昔、オブジェクト指向の勢い華やかし頃、良く分からないのに(世間的に?)多重定義だぁ、上書きだぁ、多重継承だぁ、再利用だぁ、との騒がしさに嫌気が差してC++なんて知らねぇ、と決め込んでいた。

だけどこのコロナ禍、暇が有り過ぎてVC++を触ってみたら、C++からCが呼べない! CからC++が呼べない!!

調べてみたら、どうも、装飾名が邪魔をしているらしい。この名前マングリング、あろうことか、Cコンパイラ依存、ターゲットアーキテクチャ依存、他言語依存、然もVC++のバージョン依存らしい、何てこったい。

ただ、解決策も有った。VC++に「extern "C"」を追加し、呼出す機能を「C並み」にする事。

試しに、C ⇔ C++ 間でサブルーチンを呼び出すだけのサンプルを作ってみた。(DLL、自ら使った事も作った事も無いけど、その内に・・・)

お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*------------------------------ お試し結果 -------------------------------*/

D:\vc2008\dllchk>testpp
CPRINT Message : C++ Called
C++PRINT Message : C Called
CPRINT Return from C++ : 100
C++PRINT Return from C : 10000

/*----------------------------------------------------------------------------*/


/*---- testc.c  ---------------- お試しソース -----------------------------*/

#include <stdio.h>

extern int subpp(char *);

int subc(char *s)
{
    printf("CPRINT Message : %s\n", s);
    printf("CPRINT Return from C++ : %d\n", subpp("C Called"));
    return(100 * 100);
}

/*---------------------------------------------------------------------------*/

/*---- testpp.cpp ---------------- お試しソース ---------------------------*/

#include <cstdio>

extern "C" int subc(char *);

extern "C" int subpp(char *s)
{
    printf("C++PRINT Message : %s\n", s);
    return(100);
}

void main(void)
{
    printf("C++PRINT Return from C : %d\n", subc("C++ Called"));
}

/*---------------------------------------------------------------------------*/