Let's assume that the following are presented:
- You want to write a .NET method and make it available for native programming languages.
- This .NET method takes a callback as an argument.
- You want to call this .NET method from a Delphi application.
First of all, what is a callback? According to Wikipedia, a callback is a reference to a piece of executable code, that is passed as an argument to another code. This allows a lower-level software layer to call a subroutine (or function) defined in a higher-level layer. Dealing with callbacks in the native world is fairly straightforward. Of course in .NET we have delegates which are the closest to this concept, but how do we use/pass callbacks between native and .NET? This is what this article is mainly about. Another issue in the above scenario is making the .NET method visible to the native world. This can be done by making the .NET assembly COM-Visible and here are some important steps when making a .NET COM assembly.
- Registering the assembly as a COM object.
- Placing the assembly in the Global Assembly Cache (GAC) if you want to call it from different paths/locations.
- Giving the assembly a Strong Name if you want to add it to the GAC.
The .NET part
I’ll use Visual Studio 2010 and C# to build this part. In VS2010, add a new class library project and name it CSDemoLibrary. From the project property and in the assembly information dialog, check to make this library COM-Visible.
We need to give this assembly a strong name if we want to add it to the Global Assembly Cash (GAC), to give it this name: Go to the Signing tab and check (Sign The Assembly), then from the drop down list, select <new …> and name the file CSDemoLibrary.
Because we need to pass a pointer to this assembly there is one further step, which is enabling unsafe code in this assembly by checking (Allow Unsafe Code) in the Build tab.
Now we are ready to write the code, delete the default class in the project and add new class, name it
For the sake of simplicity, in this demo, we will use a simple callback which takes just two parameters (integer and string). In Delphi this callback function will be declared like this:
procedure callback(intParam: Integer; strParam: pChar);
So, to use the equivalent of this callback in .NET, declare a delegate like this:
public delegate void NativeCallback(Int32 intParam, [MarshalAs(UnmanagedType.LPWStr)] string strParam);
I use the
[MarshalAs(UnmanagedType.LPWStr)] attribute here in order for a string to get converted into Unicode/widestring. You can omit this attribute if you want to pass just AnsiString (in that case the
strParam in the Delphi method should be
pAnsiChar). Now let’s write the .NET method. First we need to make
MyClass COM-Visible by using the
ComVisible(true) attribute with a unique GUID string and because this .NET method will deal with the native pointer (the callback pointer), we must mark it with the (
unsafe) keyword. Here is the MyClass.cs full code:
using System.Threading;namespace CSDemoLibrary
public delegate void NativeCallback(Int32 intParam,
[MarshalAs(UnmanagedType.LPWStr)] string strParam);
public class MyClass
public unsafe int Process(
IntPtr ptr = new IntPtr(callbackPointer);
NativeCallback callbackMethod =
callbackMethod(25, "first step");
callbackMethod(50, "second step");
callbackMethod(75, "third step");
callbackMethod(100, "fourth step");
return intValue * 10;
In the previous code the
Process method emulates a long running process (like getting a big chunk of data from the internet) with status notifications using callback from the native host application. In our demo and for the sake of simplicity we use
Thread.Sleep to pause the execution for one second each step to emulate a long running process that returns an integer value. In this method
callbackPointer is the native callback pointer and (
strParam) are the callback parameters. These two parameters will work as initialization parameters for the callback. In many scenarios there is no need for these initialization parameters but I keep them here to show how we can pass them from a native application to the .NET method. The
GetDelegateForFunctionPointer method is the key in this solution; it converts an unmanaged function pointer to a delegate. There is more information about this method here.
Registering the .NET assembly
To avoid paths issue, I’ll use (Visual Studio Command Prompt). There is shortcut to this tool in (Start menu-> All Programs-> Microsoft Visual Studio 2010-> Visual Studio Tools). Using this tool you can register the COM assembly by following these steps:
- Run Visual Studio Command Prompt as Administrator, then go to the CSDemoLibrary.dll folder using regular DOS commands.
- If you intend to put the .NET library CSDemoLibrary.dll in the same directory as the native application (.exe), you can ignore the step of adding the assembly to the Global Assembly Cash (GAC), else you should add it to the GAC, and to do so, use gacutil with the parameter –I, like this: gacutil -i CSDemoLibrary.dll.
- Register the COM assembly using regasm like this: regasm CSDemoLibrary.dll.
- To remove the .NET assembly from GAC: gacutil -u CSDemoLibrary.
- To unregister the .NET assembly: regasm -u CSDemoLibrary.dll.
The native (Delphi) part
I’ll use Delphi 2010 for this part. In the Delphi 2010 IDE, create a new VCL application. Add (
TGauge) to the main form.
In the code behind of the main form add
ComObj to the uses section. Make a simple callback procedure to update
Gauge1 progress and the
lbMessage caption like this:
procedure callback(intParam: Integer; strParam: pChar); stdcall;
Form2.Gauge1.Progress := intParam;
Form2.lbMessage.Caption := strParam;
I’ll use late binding in this demo, so update the
btnProcess click event handler like this:
procedure TForm2.btnProcessClick(Sender: TObject);
oleObject := CreateOleObject('CSDemoLibrary.MyClass');
ShowMessage('result= ' + IntToStr(oleObject.Process(10, LongInt(@callback), 0, 'initialization')));
except on E: Exception do
ShowMessage('COM Error: ' + #13 + #10 + e.Message);
This application calls the .NET long process which notifies the native application about the process status through the callback procedure and finally it displays the result of the .NET method/process.
Points of Interest
I hope this article shows how easy is is to consume a C# COM assembly in Delphi.