.NET COM Interop Method Signature - c#

What interop signature would you use for the following COM method? I am interested particularly in the final two parameters, and whether to try to use MarshalAs with a SizeParamIndex or not.
HRESULT GetOutputSetting(
DWORD dwOutputNum,
LPCWSTR pszName,
WMT_ATTR_DATATYPE* pType,
BYTE* pValue,
WORD* pcbLength
);
Documentation states:
pValue [out] Pointer to a byte buffer containing the value. Pass NULL
to retrieve the length of the buffer
required.
pcbLength [in, out] On input, pointer to a variable containing the
length of pValue. On output, the
variable contains the number of bytes
in pValue used.

You could try the PInvoke Signature Toolkit. It's rather useful for getting marshaling right when performing platform interops. It quite possibly won't cover your particular problem, but you may find a comparable one that gives you the information you seek.

I would use the SizeParamIndex, because your scenario is exactly the one this feature is for: To specify the length of a variable sized array.
So the last to parameters would be in C# signature:
byte[] pValue,
ref ushort pcbLength
The byte-Array is passed without ref, because the array corresponds to a pointer in native code.
If you pass NULL (or null in C#) for pValue in order to retrieve the size of the buffer needed. That means also that the caller has to allocate the byte-Array.
The parameter pcbLength is passed by ref, because it is used as an in/out-parameter.

Related

Convert c++ std::string to c# byte array

I have a .NET program that uses a DLL export to get a name of a user.
public static extern string Name(byte[] buf);
This is the export, and I would really like to not change it, as a lot of code relies on it. So, I would like to know, in C++, how would I convert a char* array to the byte buffer?
I have tried this:
void Name(std::byte buf[256])
{
std::string s{ "test" };
std::byte* ptr = reinterpret_cast<std::byte*>(s.data());
buf = ptr;
return;
}
When I print out the string that I convert, I get nothing, it is empty.
Your C++ function implementation does not match the expectations of the C# function declaration.
The C# byte array is marshalled into the function as a pinned pointer to the array's raw memory. The syntax you are using in the C++ code for the parameter (std::byte buf[256]) is just syntax sugar, the compiler actually treats it as a pointer (std::byte* buf). Which is fine in this situation. However, your C++ function is not actually copying anything into the memory that the pointer is pointing at. You are simply changing the pointer itself to point at a different memory address. And the pointer itself is a local variable, so when the function exits, the pointer will no longer exist, and it won't matter what it is pointing at.
Also, the C# declaration is expecting the function to return something that can be marshalled to a .NET string, but the C++ function is not actually return'ing anything at all. The default marshalling behavior for a string return value is as UnmanagedType.LPStr, so the C++ function needs to return a pointer to a null-terminated char* string. The memory for that string must be allocated with CoTaskMemAlloc(), as the marshaller will take ownership of the memory and free it with CoTaskMemFree() after converting the char data to a string.
Also, the C++ function has no calling convention defined, so it is going to use the compiler's default (which is usually __cdecl, unless you change your compiler's settings). However, the default calling convention that .NET's DllImport expects the C++ function to use is __stdcall instead (for compatibility with Win32 APIs). This mismatch won't matter in 64bit, but it matters alot in 32bit.
Without changing your C# declaration, try this on the C++ side:
char* __stdcall Name(std::byte buf[256])
{
std::string s{ "test" };
size_t size = s.size() + 1;
memcpy(buf, s.c_str(), size);
void *ptr = CoTaskMemAlloc(size);
memcpy(ptr, s.c_str(), size);
return ptr;
}
That being said, it is weird to have a function that returns a string in both a byte array output parameter and in a return value. Are you sure the byte array is not meant to be used as an input parameter instead, where the function parses its content to extract amd a string? That would make more sense. It would really help if you would update your question to show how your .NET code is actually calling the C++ function and using the byte array.
When you write
void Name(std::byte buf[256])
that declares buf as pointer to 256 bytes, not an array. So when you later write
buf = ptr;
all you are doing is changing the local variable buf to now point at ptr. And at the end of the function the local variable dies.
Use std::array<std::byte, 256>& and copy the string contents into that. Or even better return a freshly made array instead of an in/out parameter.

What is the difference between [In, Out] and ref when using pinvoke in C#?

Is there a difference between using [In, Out] and just using ref when passing parameters from C# to C++?
I've found a couple different SO posts, and some stuff from MSDN as well that comes close to my question but doesn't quite answer it. My guess is that I can safely use ref just like I would use [In, Out], and that the marshaller won't act any differently. My concern is that it will act differently, and that C++ won't be happy with my C# struct being passed. I've seen both things done in the code base I'm working in...
Here are the posts I've found and have been reading through:
Are P/Invoke [In, Out] attributes optional for marshaling arrays?
Makes me think I should use [In, Out].
MSDN: InAttribute
MSDN: OutAttribute
MSDN: Directional Attributes
These three posts make me think that I should use [In, Out], but that I can use ref instead and it will have the same machine code. That makes me think I'm wrong -- hence asking here.
The usage of ref or out is not arbitrary. If the native code requires pass-by-reference (a pointer) then you must use those keywords if the parameter type is a value type. So that the jitter knows to generate a pointer to the value. And you must omit them if the parameter type is a reference type (class), objects are already pointers under the hood.
The [In] and [Out] attributes are then necessary to resolve the ambiguity about pointers, they don't specify the data flow. [In] is always implied by the pinvoke marshaller so doesn't have to be stated explicitly. But you must use [Out] if you expect to see any changes made by the native code to a struct or class member back in your code. The pinvoke marshaller avoids copying back automatically to avoid the expense.
A further quirk then is that [Out] is not often necessary. Happens when the value is blittable, an expensive word that means that the managed value or object layout is identical to the native layout. The pinvoke marshaller can then take a shortcut, pinning the object and passing a pointer to managed object storage. You'll inevitably see changes then since the native code is directly modifying the managed object.
Something you in general strongly want to pursue, it is very efficient. You help by giving the type the [StructLayout(LayoutKind.Sequential)] attribute, it suppresses an optimization that the CLR uses to rearrange the fields to get the smallest object. And by using only fields of simple value types or fixed size buffers, albeit that you don't often have that choice. Never use a bool, use byte instead. There is no easy way to find out if a type is blittable, other than it not working correctly or by using the debugger and compare pointer values.
Just be explicit and always use [Out] when you need it. It doesn't cost anything if it turned out not to be necessary. And it is self-documenting. And you can feel good that it still will work if the architecture of the native code changes.

Returning a unsigned long array from C dll into C# as uint[] why is a MarshalDirectiveException error thrown?

I am using a pointer to a unsigned long array (manipulate data) then send it back to C#
in C#
[DllImport("some_dll.dll")]
private static extern uint[] func(uint[]x, uint[]y, uint[]z);
C header
_declspec(dllexport) unsigned long* _stdcall func(unsigned long[],
unsigned long[],unsigned long[]);
error
MarshalDirectiveException
Cannot marshal 'return value': Invalid managed/unmanaged type combination
Kindly let me know what is causing the problem.
The message means that the p/invoke marshaller isn't capable of marshalling that return value into a uint[].
As I see it you have the following options:
Declare the C# function as returning IntPtr. Then on the managed side you need to copy the memory into a uint[] allocated in your C# code. You can use Marshal.Copy to do that. Somehow you'll need to find out the length of the array. You also need to handle deallocation. Your C# code cannot do that. So it will have to call another function in the native code and ask the native code to deallocate.
Allocate the uint[] in the C# code before the call to your native code. Instead of using a function return value, you pass the uint[] as a parameter. This requires the calling code, the C# code, to know how big the array needs to be.
If you can choose option 2, it will result in simpler code on both sides of the interface. My guess is that the return array is the same length as the input arrays. In which case choose option 2.

client getting a byte array returned from C++ COM dll

I have this declaration in C++ COM header and IDL files:
//Header file:
#define MAX_LENGTH 320
typedef BYTE PRE_KEY [MAX_LENGTH];
//IDL file:
#define MAX_COUNT 10
HRESULT Save([in] DWORD dwCommand, [in]float fdata[MAX_COUNT], [out] PRE_KEY* phKey);
This is the C# client code:
//After C# interop compilation, the method's signature in C# becomes:
Save(uint dwCommand, float[] fdata, out byte[] phKey);
//The code to call the C++ COM server:
uint dwCommand = 2;
float[] fdata = new float[dwCommand];
fdata[0] = 1;
fdata[1] = 2;
byte[] phKey = new byte[320];
save(dwCommand, fdata, out phKey);
The code will crash in ntdll.dll before the call returns to C#, but the C++ server has already finished processing and no longer in the stack.
Anyone can figure out how to resolve this issue? And as I am using interop compilation to compile the idl file to generate the C# signaure, so I can't do something in the C++ IDL file and manually change the C# signature.
And what is funny about this is I have another similar call which returns the exact same phKey from C++ to C# and it works perfectly. The only difference is in that call phKey is in a structure and the entire structure is an '[out]' param. Really can't see why this can be returned within a structure but not directly as a param.
The [out] attribute on your IDL declaration is a serious interop problem. It means that your COM server will allocate the array and the caller needs to release it. That very rarely comes to a good end, there is no guarantee whatsoever that your server and your client use the same heap. And will always fail when you use the C runtime allocator with the malloc() function or new[] operator, the CRT uses its own private heap, one that the caller can never get to unless they share the exact same version of the CRT. Odds are very small that this is the case in general, zero when you interop through the CLR.
Which is why it bombs, the CLR knows it needs to release the array after copying it to a managed array. It will use CoTaskMemFree(), using the heap that's reserved for COM interop allocations. Surely you didn't use CoTaskMemAlloc() to allocate the array.
The general solution to this problem is to have the caller supply the array, callee fills it in. Which requires [in, out] on the parameter. And an extra parameter that indicates the size of that passed array, [sizeis] to tell the marshaller about it. Very efficient, no allocation is required. Using the automation SAFEARRAY type avoids having to specify that extra argument, the CLR knows about that type.

char * in managed code?

I have a c++ plus method that generates a value. i am calling this method from a c# application.
The C++ method is like this:
extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[])
The generate method returns an array of chars (sOut[]=returnvalue; return sOut;)
Now I'm calling this method from my c# app:
[DllImport("mydll.dll")]
static extern string generate(string sIn, string sOut);
As you can see, the return type in c# is string. What is happening is that the returned value in c# is not correct and it is corrupted. (The value is correct inside the generate method, but whenever i call it from c# to extract it, i get some erroneous value.)
Is it OK that my method in C# has a string return value while in c++ it's a char*?
Please share your comments, it is urgent, thanks.
Unfortunately, char* can be somewhat ambiguous. (Literally, it's a pointer to a single character, where you may or may not be able to get at other characters by dereferencing the pointer like an array.) Usually, though, it's a pointer to a null-terminated string of single bytes in ANSI encoding (what Microsoft calls an LPStr). By default, P/Invoke marshals instances of System.String as the BStr type, which is like a Unicode Pascal string (with a length at the beginning). You'll need to use the MarshalAs attribute to explicitly specify how you want the strings marshaled between managed and unmanaged code.
Most likely, you'll want to declare it like so:
[DllImport("mydll.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
static extern string generate(
[MarshalAs(UnmanagedType.LPStr)] string sIn,
[MarshalAs(UnmanagedType.LPStr)] out string sOut);
However, your function signature is confusing: is it returning the string, or is it setting an output variable? You may have to adjust the P/Invoke declaration to fit your needs.
Is it OK that my method in C# has a
string return value while in c++ it's
a char*?
It depends really.
If that C++ method is allocating the memory for the string and expecting the caller to deallocate it then you have problems...big problems. You could try calling Marshal.FreeHGlobal on it, but there is no guarentee that the C++ method actually allocated the memory on the global heap. Most APIs have you feed the pointer back into one of their methods to free the memory.
It is also possible that the char * is pointing to memory which does not need to be freed. In that case using a string should be fine.
You could have the C# declaration return an IntPtr and then call one of the Marhsal.PtrToStringXXX methods.
This method cannot be called safely from a native C++ application either. It returns a pointer to an array on the stack frame. Any call to another function will overwrite that stack frame and corrupt the array.
This probably works by accident in a C++ program right now because there is no function call after obtaining the return value of this function. This kind of luck is no longer available when the P/Invoke marshaller sits in between.
You will have to redesign the C++ function. You should give it arguments that allows the caller to pass its own buffer to be filled with the result. For example:
extern "C" void __stdcall generate(const char* input, char* output, int outputSize)
Call this in C# code by passing a StringBuilder for the output argument, properly initialized with a sufficient Capacity to receive the string. The outputSize argument ensures that the C++ function can avoid writing past the end of the buffer and corrupt the garbage collected heap. Don't ignore it.

Categories

Resources