user3104842
user3104842

Reputation:

How to convert C function signature to C# and call that function in the native DLL?

int __stdcall getProps( /* [ in ] */ void * context,
                        /* [ in ] */ const UINT32 index,
                        /* [ in ] */ WCHAR * const pwszFilterPath,
                        /* [ in, out ]*/ UINT32 * const pFilterPathSizeInCch,
                        /* [ in ]*/ WCHAR * const pwszFilterName,
                        /* [ in, out ] */ UINT32 * const pFilterNameSizeInCch );

Is this correct C# signature for a C function whose signature is above:

[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
        public static extern int getProps( IntPtr context,
                                           uint index,
                                           [MarshalAs(UnmanagedType.LPWStr)]
                                           out StringBuilder pwszFilterPath,
                                           ref uint pFilterPathSizeInCch,
                                           [MarshalAs(UnmanagedType.LPWStr)]
                                           out StringBuilder pwszFilterName,
                                           ref uint pFilterNameSizeInCch );

There is something wrong here and I do not know what (I do not know C#). Sometimes when I get it to work, I receive ANSI strings inside StringBuilder variables and they should contain UNICODE characters.

The idea is to call this function twice. First with pwszFilterPath and pwszFilterName set to NULL, in order to retrieve the required size, then allocate memory for these two buffers and then retrieve the values on the second call. I know how to do it in C/C++, but not in C#.

Upvotes: 2

Views: 928

Answers (1)

David Heffernan
David Heffernan

Reputation: 612844

You must remove the out keyword from StringBuilder parameters. A plain StringBuilder parameter is marshalled as a pointer to character array. If you want the native code to receive NULL, pass C# null.

[DllImport("myLib.dll", CharSet = CharSet.Unicode)]
public static extern int getProps(
    IntPtr context,
    uint index,
    StringBuilder pwszFilterPath,
    ref uint pFilterPathSizeInCch,
    StringBuilder pwszFilterName,
    ref uint pFilterNameSizeInCch
);

The call pattern is as follows:

  1. Pass null to the StringBuilder parameters to find out the buffer size.
  2. Allocate StringBuilder instances using the constructor overload that receives the capacity.
  3. Call the function again passing the StringBuilder instances.

Perhaps like this:

uint filterPathLength = 0;
uint filterNameLength
int retval = getProps(
    context,
    index,
    null,
    ref filterPathLength,
    null,
    ref filterNameLength
);
// check retval

StringBuilder filterPath = new StringBuilder(filterPathLength);
StringBuilder filterName = new StringBuilder(filterNameLength);
int retval = getProps(
    context,
    index,
    filterPath,
    ref filterPathLength,
    filterName,
    ref filterNameLength
);
// check retval

And you'll need to make sure you get the length convention right for null-terminator. I cannot tell whether or not your function returns lengths that include the null-terminator.


As @Ben points out, your annotations don't match the text description.

/* [ in ] */ WCHAR * const pwszFilterPath

The [ in ] should imply that the data is flowing into the function. When that is so, the type should be const WCHAR*. But it's not the case. This is an out parameter. So the code should read:

/* [ out ] */ WCHAR * const pwszFilterPath

Upvotes: 3

Related Questions