VBフォーム(Windows アプリケーション)だって、コマンドプロンプトでDLL?

|

ずぼらでめんどくさがり屋には、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

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