Visual Basic DLL

Creating a MinGW DLL for Use with Visual Basic

Below are two examples of techniques which facilitate the use of use of MinGW to create DLLs, exporting functions which may be called from Visual Basic. The first of these uses C or C++ as the implementation language; the second uses GNAT/GCC.

Example 1: A DLL for Visual Basic, Programmed in C or C++

NOTE: This description is only for Visual Basic 6, not DotNet.

Step 1: Create your DLL.

VB can only call __stdcall functions, not __cdecl functions. So we must export all our functions as __stdcall. Create a DLL with the following code template:

When compiling, add "--add-stdcall-alias" to your linker flags. If you're passing linker flags via g++, it should be "-Wl,--add-stdcall-alias". This adds an undecorated alias for the exported function names that is simply the name of the function. By default, the functions are exported with an @nn appended to the function name.

The DllMain function is called by the system when your dll is loaded and unloaded from a process, as well as when threads attach and detach (I'm not sure what that means, but it's in the case statement). If you have anything important to do in your dll when these events occur, this is the place to do it. Initializing WinSock is a popular thing to do here so that the WinSock function calls don't all return 10093 -- WSANOTINITIALISED. If you don't export this function yourself, it won't be automatically exported as in Visual Studio.

(1) : Internally (under VB), a string is a BSTR: a pointer to an array of WCHAR with a 32 bit prefix that declares the number of elements of the array. When calling an API, VB passes a LPSTR (converted from the WCHAR array). A WCHAR array is not a LPWSTR since it can contain embedded NULL WCHARs.

Example 2: Programming a GNAT/GCC + MinGW DLL for Visual Basic

This is an extension of Roger Pearse's The Noddy Guide to using ADA code with Visual Basic. Roger effectively gives up on the possibility of a Windows DLL under GNAT/GCC. Windows has improved somewhat since his original article. So has gcc. The following outlines my attempt.

The spec, API.ADS:

function Factorial, which accepts a Win32.LONG and returns a Win32.LONG (I gave up on C.int as it was too small for a Factorial of 16)

function GetCount, which returns the value of the global Count (another Win32.LONG)

procedure Mangle, which accepts a Win32.LPWSTR and attempts to modify it _in situ_

I'm still working through the issue of what to do with Mangle. Perhaps two routines are needed. The first to ask the DLL how much space is needed, with VB the allocating it, and the second then actually poking the data into the allocated space.

Pattern returns a pointer to the string data. I rediscovered the technique for reading it out and that's in the VB code below. It also seems that, for as long as the DLL is loaded, the data stored in foo is available for transfer.

project Build is
for Source_Dirs use ( "./", "c:\mingw\lib\gcc\mingw32\4.2.1\**" );
for Object_Dir use "debug";
for Exec_Dir use "debug";
for Main use ("api");
type Style_Type is ("debug", "release");
Style : Style_Type := external ("STYLE", "debug");
case Style is
when "debug" =>
for Object_Dir use "debug";
when "release" =>
for Object_Dir use "release";
for Exec_Dir use ".";
end case;
package Builder is
case Style is
when "debug" =>
for Default_Switches ("Ada")
use ("-g");
when others =>
null;
end case;
end Builder;
package Compiler is
case Style is
when "debug" =>
for Default_Switches ("Ada")
use ( "-s",
"-gnata",
"-gnato",
"-gnatE");
when "release" =>
for Default_Switches ("Ada")
use ("-O2", "-fno-strict-aliasing");
end case;
end Compiler;
end Build;

Now the VB code. Take note of a few things:

Exporting as DLL generates decorated names, but dlltool was able to resolve the decorated ones with the undecorated ones specified in api.def

For reasons I remember reading somewhere, initialize_api and finalize_api are mandatory, and must be the first and last calls to the DLL

Seeing as _woof woof woof woof_ is longer than _foo man chew_,

s

ends up containing _foo man chew f woof_

As mentioned before, I rediscovered some code for getting data from a pointer and put that in. It's the PointerToString function at the bottom.