Merging modern software development with electrons and metal
Random header image... Refresh for more!

Posts from — October 2007

VB6 Notes – Determining the calling convention of a C DLL

OK, this isn’t just for VB6, but I needed to call a function in a C DLL from VB6, and VB6 can only call functions that use the stdcall calling convention.

A properly written C DLL is easy to use from VB6, but a C DLL not written with VB6 in mind may require an adapter layer. C++ DLLs are typically very difficult to use from another compiler (or VB), due to incompatibilities in areas such as name mangling. Of course, COM DLLs and .NET assembly DLLs are totally different cases. COM objects are almost always easy to call from VB6. .NET assemblies are like C DLL’s: when properly designed with a COM interop, using them is like using a COM object, but if they’re not designed with COM in mind, then they can’t be used.

If you have the source or header files, you should be able to determine the calling convention – although you may have to know a bit about the compiler, too (so you know what the default convention is, and how each convention is identified).

A second method that sometimes works is to use Microsoft’s dumpbin program. You can get dumpbin for free by downloading and registering Visual C++ 2005 Express Edition (notes on Win32 development here) . By dumping the Export list and looking at the decorated names, you can determine the calling convention. I got the idea for this approach from here.

For a function defined as void foo(int a), its decorated name would be:

  • _foo for __cdecl (C calling convention)
  • _foo@4 for __stdcall (Windows calling convention)
  • @foo@4 for __fastcall

I used dumpbin /EXPORTS path_to_dll from the Visual C++ 2005EE command prompt. However, not all DLLs export the decorated names, even for DLLs created with MS VC++. I’ve seen some that do and some that don’t; it might have to do with how the exported names are declared (e.g. in a DEF file). Non-Microsoft C compilers (MingW, gcc, Borland, etc) might decorate names differently.

By the way, you can use dumpbin /DEPENDENTS path_to_DLL to see what DLLs a DLL requires.

Another way is to use Python and the ctypes module. The ctypes module is included in Python 2.5; it’s an add-on to earlier versions. ctypes can load a DLL as either a Windows (stdcall) DLL using windll.LoadLibrary or a C (cdecl) DLL using cdll.LoadLibrary.

Here’s an example using Python and two functions in the MEI PC/DSP 2.5.09 MEDVC60F.DLL. One function (dsp_init) is stdcall; the other function (find_pci_dsp) is cdecl.

>>> from ctypes import *
>>> stdcall = windll.LoadLibrary(‘C:\\medvc60f.dll’)
>>> cdecl = cdll.LoadLibrary(‘C:\\medvc60f.dll’)
>>> ShortArray20Type = c_short * 20
>>> addr = ShortArray20Type()
>>> irq = ShortArray20Type()
>>> numBoards = c_short(0)
>>> cdecl.find_pci_dsp(byref(numBoards), byref(addr), byref(irq))
>>> stdcall.find_pci_dsp(byref(numBoards), byref(addr), byref(irq))
Traceback (most recent call last):
File “<interactive input>”, line 1, in <module>
ValueError: Procedure probably called with too many arguments (12 bytes in excess)
>>> cdecl.dsp_init(0x300)
Traceback (most recent call last):
File “<interactive input>”, line 1, in <module>
ValueError: Procedure called with not enough arguments (4 bytes missing) or wrong calling convention
>>> stdcall.dsp_init(0x300)

October 25, 2007   No Comments