Search code examples
c#.netvb.netdelphipinvoke

Translate Delphi export to C#/Vb.Net


I'm writting a simple wrapper of the Subtitle Workshop library, but I get stuck translating this Delphi export to C# or Vb.Net:

procedure GetSubtitleText(Time: Integer; Text: {$IFDEF UTF8}PWideChar{$ELSE}PChar{$ENDIF}; var BufferLen: Integer); stdcall;
var
  FText : {$IFDEF UTF8}WideString{$ELSE}String{$ENDIF};
begin
  FText := DisplaySubtitle(Subtitles, Time);

  if BufferLen > 0 then
{$IFDEF UTF8}
//    Text := Copy(FText, 1, BufferLen)
    Text := StringToWideChar(FText, Text, BufferLen)
{$ELSE}
    StrLCopy(Text, PChar(FText), BufferLen)
{$ENDIF}
  else
    BufferLen := Length(FText);
end;

I'm not understanding how to translate the conditionals of the Text parameter.

This is what I did:

<SuppressUnmanagedCodeSecurity>
<DllImport("SubtitleAPI.dll", EntryPoint:="GetSubtitleText", CharSet:=CharSet.Ansi)>
Friend Shared Sub GetSubtitleText(
       <MarshalAs(UnmanagedType.I4)> ByVal time As Integer,
       <MarshalAs(UnmanagedType.LPStr)> ByVal text As StringBuilder,
       <MarshalAs(UnmanagedType.I4)> ByRef bufferLength As Integer
)
End Sub

It seems to work as expected, however, I would like to ensure that I wrote it correctly.


Solution

  • Your translation is fine, so long as UTF8 is not defined when the Delphi code was compiled. If UTF8 is defined then you would need to marshal as LPWStr. And I have to assume that the Delphi code is compiled with an ANSI version of Delphi, so that PChar maps to PAnsiChar. The Delphi code will be totally broken if you try to compile it with Delphi 2009 or later.

    There's not much point in specifying CharSet.Ansi if you explicitly mark every parameter with an explicit MarshalAs attribute.

    The Delphi code is quite odd. The author seems not to know the difference between UTF-8 and UTF-16. In Delphi PWideChar and WideString are UTF-16 encoded. The call to StrLCopy passes the wrong buffer length, it should pass BufferLen - 1 due to a very poor design of that RTL function. The code should also set BufferLen to the number of characters copied, but does not.

    You can work around that in your .net code. I'll write C# because that's what I know. I'm sure that you will understand it:

    int len = 0;
    GetSubtitleText(time, null, len);
    StringBuilder sb = new StringBuilder(len);
    len++; // work around poor design/use of StrLCopy 
    GetSubtitleText(time, sb, len);
    

    On the face of it, I would be somewhat sceptical of this Delphi code. You should be on your guard. At least you have the source code for it!