$theTitle=wp_title(" - ", false); if($theTitle != "") { ?>
About Virtualization, VDI, SBC, Application Compatibility and anything else I feel like
I write most of my code in unmanaged languages such as Delphi and C/C++. Sometimes customers ask me to interface my code to their .net code in which case I create a dll for them.
A recurring thing is that I need to return string to .net.
There are many ways to do this of course but in all cases we need to manage memory: who will allocate the memory for the string and who is responsible for freeing it?
Windows API Method
Windows API often requires the caller to pass in an allocated buffer and it’s maximum size and returns the actual size. An example is the GetComputerName API:
1 2 3 4 | BOOL WINAPI GetComputerName( _Out_ LPTSTR lpBuffer, _Inout_ LPDWORD lpnSize ); |
A typical call to this API looks like this in .net (VB.NET example):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Imports System.Runtime.InteropServices Imports System.Text Module Module1 <DllImport("kernel32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Unicode, SetLastError:=True)> _ Public Function GetComputerName( _ <[In]()> <[Out]()> <MarshalAs(UnmanagedType.LPWStr)> ByVal Name As StringBuilder, _ <[In]()> ByRef CharCount As Integer) _ As Boolean End Function Sub Main() Const MAX_COMPUTERNAME_LENGTH As Integer = 15 Dim Buffer As New StringBuilder(MAX_COMPUTERNAME_LENGTH) Dim bRes As Boolean Dim iLen As Integer iLen = MAX_COMPUTERNAME_LENGTH bRes = GetComputerName(Buffer, iLen) Buffer.Length = iLen If bRes Then System.Console.WriteLine("Computername: " & Chr(34) & Buffer.ToString() & Chr(34)) End If End Sub End Module |
As you can see we need to allocate the string before the API call and set the correct length after the API call. For that reason I used the StringBuilder class because the regular .net string class does not have a way to change the string length.
BSTR Method
For this reason I prefer to use the BSTR type because it has much easier memory allocation and deallocation.
An example using ATL’s CString class in C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | BOOL __stdcall ReturnString(BSTR* TheString) { CString s = _T("Hello World!"); try { *TheString = s.AllocSysString(); return TRUE; } catch (CAtlException Exception) { return FALSE; } } |
And an example implementation in VB.NET:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | Imports System.Runtime.InteropServices Module Module1 <DllImport("mydll.dll", CallingConvention:=CallingConvention.StdCall)> _ Public Function ReturnString( _ <[Out]()> <MarshalAs(UnmanagedType.BStr)> ByRef TheString As String) _ As Boolean End Function Sub Main() Dim bRes As Boolean Dim TheString As String = Nothing bRes = ReturnString(TheString) If bRes Then System.Console.WriteLine("The String " & TheString) End If End Sub End Module |
Below an example in Delphi for the ReturnString implementation (VB code stays the same):
1 2 3 4 5 6 7 8 | function ReturnString(var TheString: WideString): Boolean; stdcall; begin try TheString := 'Hello World'; finally Result := True; end; end; |
Note: In all examples I am not passing the string as the function return value because of compiler implementation differences regarding the calling convention for the return value. A good description can be found here on stackoverflow.
3 Responses for "Returning a string from unmanaged dll to .net"
In the final Delphi example you declare the function like this:
function ReturnString(TheString: WideString): Boolean;
Isn’t that a mistake. Surely it needs to be an out parameter.
Need to check but I thought that Delphi passes a WideString by reference (since it’s basically a pointer)…
Well, I’ve not tested but I’m 100% sure your version won’t work because you need to pass by ref.
Leave a reply