1

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?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Marino Šimić
  • 7,318
  • 1
  • 31
  • 61
  • 2
    Try with IntPtr instead, and Marshal.PtrToStringAnsi. – aybe Feb 10 '14 at 00:16
  • You got it Aybe. "int get_FilterName(int Index, out IntPtr Name);" is the interop, and PtrToStringAnsi gives what I hoped for :) Please write it as answer. – Marino Šimić Feb 10 '14 at 00:22

2 Answers2

2

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • As you said I cannot modify the interface because it is part of the filter i'm using in direct show. But thanks for the explanations. – Marino Šimić Feb 10 '14 at 15:21
1

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#

Community
  • 1
  • 1
aybe
  • 15,516
  • 9
  • 57
  • 105