Question

I am unable to translate the interface definition below from Delphi to C#:

  IDCDSPFilterInterface = interface(IUnknown)
    ['{BD78EF46-1809-11D6-A458-EDAE78F1DF12}']
    // removed functions thjat are already working
    function get_FilterName(Index : integer; out Name : PChar): HRESULT; stdcall;
  end;

I have tried with StringBuilder in the following way:

[ComVisible(true), ComImport, SuppressUnmanagedCodeSecurity,
Guid("BD78EF46-1809-11D6-A458-EDAE78F1DF12"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDCDSPFilterInterface : IBaseFilter
{
    [PreserveSig]
    int get_FilterName(int Index, [MarshalAs(UnmanagedType.LPStr)] StringBuilder Name);
}

I tried with LPStr, LPWStr, which both gives garbage characters in the string builder, and LPTStr which fails with an error message saying that this kind of marshalling combination is not allowed.

The definition of the method in Delhi is:

function TDCDSPFilter.get_FilterName(Index : integer; out Name : PChar): HRESULT; stdcall;
begin
{$IFDEF EXCEPT_DEBUG}try{$ENDIF}
  FcsFilter.Lock;
  try
  {$IFDEF WITH_INTERNAL_DSP}
    Result := S_FALSE;
    if (Index < 0) or (Index > fFilters.Count -1) then Exit;
    Name := PChar(fFilters.Name[Index]);
    Result := S_OK;
  {$ELSE}
    Result := E_NOTIMPL;
  {$ENDIF}
  finally
    FcsFilter.UnLock;
  end;
{$IFDEF EXCEPT_DEBUG} except er('TDCDSPFilter.get_FilterName'); end; {$ENDIF}
end;

The fFilters.Name is declared as:

property Name[Index: integer]: String read GetName;

All my other interface methods work well with other basic types (in and ref) except this one with PChar output.

I get S_OK but the string in the StringBuilder is garbage...

I know the method is properly called because if I pass the wrong indexes I get S_FALSE (as the method body is defined to do).

Can anybody help to give the proper Marshalling for the Delphi out PChar?

Was it helpful?

Solution 2

Try to use IntPtr for Name parameter then get the content of the string with Marshal.PtrToStringAnsi.

References :

Marshal "char *" in C#

char* to a string in C#

OTHER TIPS

That is a rather poorly designed interface. This is a COM interface and so it should use the COM string, BSTR.

However, as it stands, the C# side has to marshal the PChar as an out parameter of type IntPtr. The declaration should be:

[PreserveSig]
uint get_FilterName(int Index, out IntPtr Name);

Then the string can be recovered by calling Marshal.PtrToStringAnsi or Marshal.PtrToStringUni depending on the encoding of the string.

You cannot use StringBuilder because that is for the situation where the caller allocates the buffer. And that's not happening here.

As I said, using a COM string would be better. The code looks like this:

Delphi

function TDCDSPFilter.get_FilterName(Index: integer; 
    out Name : WideString): HRESULT; stdcall;

C#

[PreserveSig]
uint get_FilterName(
    int Index,
    [MarshalAs(UnmanagedType.BStr)]
    out string Name
);

Of course, that assumes that you can modify the interface. Quite likely you cannot. In which case you are stuck with out IntPtr.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top