Returning a string from unmanaged dll to .net

I write most of my code in unmanaged languages such as Delphi and C/C++. Sometimes I need to interface my code to .net code in which case I create a dll.

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:

BOOL WINAPI GetComputerName(
  _Out_    LPTSTR lpBuffer,
  _Inout_  LPDWORD lpnSize
);

A typical call to this API looks like this in .net (VB.NET example):

Imports System.Runtime.InteropServices
Imports System.Text


Module Module1
     _
    Public Function GetComputerName( _
           ByVal Name As StringBuilder, _
         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:

BOOL __stdcall ReturnString(BSTR* TheString)
{
	CString s = _T("Hello World!");
	try
	{
		*TheString = s.AllocSysString();
		return TRUE;
	}
	catch (CAtlException Exception)
	{
		return FALSE;
	}

}
Imports System.Runtime.InteropServices

Module Module1
     _
    Public Function ReturnString( _
         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). Note that in all examples I am not passing the string as the function return value because of compiler implementation differences as described here.

function ReturnString(TheString: WideString): Boolean; stdcall;
begin
  try
    TheString := 'Hello World';
  finally
    Result := True;
  end;
end;