Integrating .NET and Win32 DLLs (written in Borland C++ Builder)

About a week ago I had to write a wrapper to win32 DLL (written in Borland C++ Builder) to use it under Microsoft .NET platform. I've spend some time googling around, trying different things and finally I was able to do exactly what I wanted. Because it took me quite some time to gather all information together I thought it can be useful for somebody else. So here it is:

Problem:

1. We have a win32 DLL. We own this DLL, so we can add some interface functions if we want to.
2. We need to create several instances of this DLL inside .NET application.
3. We need to be able to pass back and forth to/from managed code complicated structures with integer, char arrays etc.
4. We need to be able to have .NET callbacks which we will be calling from that win32 DLL.

Follow up:

Solution:

1. Calling DLLs functions from .NET assembly:

This part is relatively easy and well understood. You need to declare functions in your header file using something like this:

extern "C" __declspec(dllexport) int SomeDLLInterfaceFunction (void* ref, SOME_STRUCTURE* ptr);

In .NET you declare them using DllImport:

[DllImport("YOUR_DLL_NAME.dll")]
private static extern Int32 _SomeDLLInterfaceFunction (IntPtr ref, SOME_STRUCTURE ptr);

We will talk about converting C-structures to .NET managed classes and also about mapping scalar types from C/C++ to .NET a bit later. Note that in .NET you use '_' to prefix your function name. This depends on your compiler options - check them to see whether you will need to use this prefix or external name will be without it.

2. Calling .NET callbacks from DLL:

In .NET you need to create a delegate function pointer type:

delegate void NETCallBackFunctionType(Int32 status, StringBuilder message);

In C++ you also declare similar function pointer type:

typedef void (*ExternalCallbackType) (int status, char* message);

Than in .NET just need to create an event handler and send pointer to that handler to a DLL:

// Internal event handlers
private void OnProcessing(Int32 status, StringBuilder message)
{
  // Do something...
}

NETCallBackFunctionType delegateOnProcessing = new NETCallBackFunctionType(OnProcessing);
_SomeDLLInterfaceFunction(delegateOnProcessing, ...);

Calling this function from DLL is obvious.

3. Scalar types mapping:

For integer types Borland uses 32 bits for both int and long. .NET has specific types that I suggest using, so you have clearer understanding how big certain parameters are: Int32, Int64, UInt32, UInt64 etc.

For any pointer I suggest using IntPtr in .NET and void* in Borland, and than do type conversion when it's necessary.

For char* variables that you need to pass from DLL to .NET I found that StringBuilder works just fine.

4. Structures mapping:

This task is actually divided to two separate ones. To send structured information from .NET to DLL you need to use Marshalling and declare C-structures like this:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
internal class SOME_STURCTURE {

  [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]
  public string stringVar1;

  public uint intVar2;
  public bool boolVar3;

  public SOME_STURCTURE()
  {
    this.stringVar1 = "";
    this.intVar2 = 0;
    this.boolVar3 = false;
  }
};

This will correspond to C-structure:

typedef struct {
  char stringVar1[32];
  int intVar2;
  bool boolVar3;
} SOME_STURCTURE;

And when you need to use this class in .NET it will look like this:

SOME_STURCTURE data = new SOME_STURCTURE();
data.stringVar1 = "SOME DATA";
data.intVar2 = 2;
data.boolVar3 = true;
_SomeDLLInterfaceFunction(delegateOnProcessing, data);

But when you need to retrieve similar structure from DLL to .NET code you will need to use a bit more complicated logic:

SOME_STURCTURE res = new SOME_STURCTURE();
IntPtr p = Marshal.AllocHGlobal(35); // Make sure you allocate space for the whole structure!

_SomeDLLInterfaceFunction(delegateOnProcessing, p);
Marshal.PtrToStructure(p, res);

That's pretty much covers my problems. If you think I missed something or if you have any questions - feel free to drop me email. It has been tested and used under Microsoft Visual Studio 2005 (.NET 2.0) and RAD Studio 2007 (formerly known as Borland C++ Builder).

No feedback yet

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)