P/Invoke passing struct longer than native code expects - c#

If I have a native code that expects a structure with two fields:
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern int my_method(ref MyStruct myStruct);
// this is what native code expects
public struct MyStruct {
IntPtr First;
IntPtr Second;
}
but instead I pass another struct to it, will it work or not - by design or by accident?
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern int my_method(ref MyLongerStruct myLongerStruct);
// this is what I want to pass to it
public struct MyLongerStruct {
IntPtr First;
IntPtr Second;
object ObjectPointer;
}
Will the object reference added to the end of the struct at C# side somehow affect P/Invoke call?

I shouldn't work. And even more, you need to add and properly set StructLayoutAttribute to the structure, as it explained here
I think, the result should be like this:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
IntPtr First;
IntPtr Second;
}

If the total difference in structure is fields added to the end, and you use StructLayout to prevent the compiler from optimizing the memory layout of your struct (as Alex Butenko suggests), then it's unlikely that there will be any negative side effects apart from a slight speed hit.
When you pass a managed struct to an external function via P/Invoke (using the DllImport attribute) there is a marshaling phase that converts your struct to a compatible format for the target. For ref and out parameters the temporary is converted back when the invoked function returns, copying the values back to your struct instance. All of this is abstracted away, although exactly how the marshaling is performed for each member can be tweaked with the right attributes.
This is how the .NET framework handles strings in P/Invoke. Since it can't just send a string instance pointer to an API function that is expecting a char * (the two are nothing alike) there has to be some translation.
The fun part is that the marshaling code doesn't know anything about what the target is expecting other than what you tell it at the C# end, so if you are sending an extended version of the structure it will do the whole thing. At the other end the native code will get a pointer to a memory block containing the information it's expecting, and it won't have any way to tell that there is more after the end of the structure.
Apart from that, no problem... as long as you're passing by reference and not by value. Passing structs by value is something that should raise big red stop signs all over your brain. Don't do it, it's evil.

Related

When calling c++ function from windows form gets error

My c++ function is given below
# define MyFunction _declspec(dllexport)
extern "C" {
MyFunction int SubtractNumbers(int a, int b)
{
return a - b;
}
MyFunction const char* GetStringKey()
{
return "My String";
}
}
Calling c++ function from windows form is given below,
[DllImport(cppFunctionsDll, CallingConvention = CallingConvention.Cdecl)]
private const string DllFilePath = #"D:\\Projects\\December\\17-12-2020\\project-device-
setup\\Debug\\AccurynCPP.dll";
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int SubtractNumbers(int a, int b);
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();
public void GetSubtract()
{
lblSubtract.Text= Convert.ToString(SubtractNumbers(1, 2));
}
public void GetStringFunction()
{
lblstringKey.Text= GetStringKey();
}
From the above function, GetSubtract() worked perfectly. But GetStringKey() not worked. when it reach on it's function while debugging, it automatically cancelled running mode in visual studio. How can i fix this issue.
The calling convention describes to the compiler and linker how parameters, the stack and registers are to be handled. So if these don't match things won't work or memory can be corrupted. For instance, if the caller thinks the first parameter should be passed in the register eax, but the callee thinks it's on the stack. Things will obviously not work. For more info on calling conventions Wikipedia has a good description here. The calling convention you choose isn't very important, unless the compiler forces one on you. For instance for 64 bit programs Microsoft only uses one to my knowledge. So, I suggest adding __cdecl in front of the functions you have written. And using CallingConvention.Cdecl on the DLLImport lines.
Also, all the data types must exactly match. For instance:
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();
This says it is returning a string, however despite our using the term string to represent a series of characters followed by a null. In this case string is a formal type. Which is they type std::string. Which is not what is being returned by the C code. The C code is returning a pointer to a char.
Generally passing complex types based on a library is kinda iffy. That's because it requires both languages to use the exact data structure to represent the type. So when crossing language boundaries it's often useful to stay with your own custom structures and very primitive types.
So I recommend your C function be this instead:
__cdecl bool GetStringKey(char *buffer, int maximumLength)
{
// Write code here to copy all the characters from your "My String"
// to the buffer or use a standard library function for copying char * strings (often called C strings) when copying make sure to copy the null character at the end. It's definitely needed.
return true; // Yes it fit
}
You will need to change your DllImport to match. Once that is working you can add code to never go over the maximum buffer length (to prevent memory corruption) and maybe change the bool to the length of the string or -1 for failure.
I suggested this particular function prototype because it doesn't pass memory between languages. Instead the C code fills in a buffer from the C++ code. Which is the safest way to do something. Passing memory between unrelated modules (in this case C and C++ doesn't always work). Though I suspect in this case your C and C++ compiler and libraries are enough alike. That you could just switch to returning a std::string. As long as both sides match perfectly.

typedef void* alternative in c#

I am using DLL runtime which is made with C language into C#.
I came across below statement.
typedef void *JCCP_PROPERTY_HANDLE;
In function it is being used as:
JCCP_RESULT __JCCP_FUNCTION__ jccpGetProperty(
JCCP_HANDLE hjccp,
const char *name,
JCCP_PROPERTY_HANDLE *phproperty);
Now I want to call jccpGetProperty() method in my C# code.
Can anybody tell how can I pass third parameter(JCCP_PROPERTY_HANDLE *phproperty) to function from C#.
I tried with below code but not working.
Extern Method:
[DllImport(DLL_NAME, EntryPoint = "_jccpGetProperty", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jccpGetProperty(IntPtr hjccp, string name, ref IntPtr JCCP_PROPERTY_HANDLE);
Usage
IntPtr handle = IntPtr.Zero;
string tag = "server.version";
var result = jccpGetProperty(hjccp, tag, ref handle);
Can anybody help me in this?
IntPtr is the correct type mapping for void*. The native type void* is generally used for an opaque pointer, and that is mapped to IntPtr in C#.
Those parts of the p/invoke declaration that we can verify are correct. The unverifible parts are:
The calling convention. You believe that it is cdecl, but we can't check.
The return type. You believe it to be pointer sized. Again we cannot check. My guess is that a 32 bit integer, int or uint is more likely. That would make a difference in a 64 bit process.
The values passed to the function. It's perfectly possible that the function is declared correctly, but you are passing invalid values.
Because you only showed partial code and details, it's hard to say much more. You will have to verify all the parts of the program that we cannot.
I suggest that you start with working C or C++ code and translate that, looking for the first point of deviation in behaviour between that code and your C# translation.

Why can't I do Marshal.SizeOf() for this C# struct?

I try to call the code int size = Marshal.SizeOf(typeof(MyStruct)) but it throws the following exception:
Type 'MyStruct' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
My struct is as follows:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.U4)]
public UInt32 version;
[MarshalAs(UnmanagedType.FunctionPtr)]
public IntPtr Start;
[MarshalAs(UnmanagedType.FunctionPtr)]
public IntPtr Stop;
// And a bunch more IntPtr, all declared the same way.
}
The struct is supposed to be passed to C-land, where the C code will use its contents as function pointers. I can't see how computing a size would fail, can anyone help?
UnmanagedType.FunctionPtr requires the field to be a delegate type. It will be a function pointer on the C-side after the structure is marshaled. Using [MarshalAs] is superfluous, a delegate already gets marshaled like that. So, roughly:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.U4)]
public UInt32 version;
public Action Start;
public Func<bool> Stop;
// etc..
}
Change the delegate types to match the function signature of the corresponding C function pointer. You often have to declare your own delegate type so you can give it the [UnmanagedFunctionPointer] attribute to match the calling convention of the C function. Usually CallingConvention.Cdecl, not the default of Stdcall.
You have to be very careful when you initialize a structure like this. The delegate objects you create and assign to the fields must be referenced elsewhere to prevent them from getting garbage collected. Either by storing them in a class object that's guaranteed to live as long as the C code can make calls, by storing them in a static variable or by explicitly adding a reference with GCHandle.Alloc()
Lots of ways to shoot your foot, good luck with it :)

PInvoke: Issue with returned array of doubles?

I am using PInvoke to call a C++ function from my C# program. The code looks like this:
IntPtr data = Poll(this.vhtHand);
double[] arr = new double[NR_FINGERS /* = 5 */ * NR_JOINTS /* = 3*/];
Marshal.Copy(data, arr, 0, arr.Length);
With Poll()'s signature looking like this:
[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(IntPtr hand);
The C-function Poll's signature:
extern "C" __declspec(dllexport) double* Poll(CyberHand::Hand* hand)
Unless I'm having a huge brain failure (admittedly, fairly common for me), this looks to me like it should be working.
However, the double values I am getting are completely incorrect, and I think this is because of incorrect memory usage. I have looked it up, and I think doubles in C# and C++ are identical in size, but maybe there is some other issue playing here. One thing that rubs me the wrong way is that Marshal.Copy is never told what type of data it should expect, but I read that it is supposed to be used this way.
Any clues, anyone? If needed, I can post the correct results and the returned results.
You are missing the CallingConvention property, it is Cdecl.
You really want to favor a better function signature, the one you have is extremely brittle due to the memory management problem, the required manual marshaling, the uncertainty of getting the right size array and the requirement to copy the data. Always favor the caller passing a buffer that your native code fills in:
extern "C" __declspec(dllexport)
int __stdcall Poll(CyberHand::Hand* hand, double* buffer, size_t bufferSize)
[DllImport("foo.dll")]
private static extern int Poll(IntPtr hand, double[] buffer, int bufferSize)
Use the int return value to report a status code. Like a negative value to report an error code, a positive value to return the number of elements actually copied into the buffer.
You shouldn't even need to marshal the data like that, as long as you declare the P/Invoke correctly.
If your CyberHand::Hand* is in reality a pointer to a double, then you should declare your P/Invoke as
[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(double[] data);
And then just call it with your array of doubles.
If it isn't really an array of doubles, then you certainly can't do what you're doing.
Also, how does your 'C' function know how big the array will be? Is it a fixed size?
The IntPtr return value will be a problem. What is the double* pointing to? An array or a single item?
You could find that it's easier (if you can) to write a simpler more friendly 'C' wrapper for the function you're calling, and call the wrapper function itself. You can of course only do that if you can change the source code of the 'C' DLL. But without knowing exactly what your function does, I can't give you specific advice.
[EDIT]
Ok, your code should theoretically work if the memory being passed back isn't being messed around with (e.g. freed up). If it's not working, then I suspect something like that is happening. You'd definitely be better writing a wrapper 'C' function that fills in an array allocated by the C# and passed to the function, rather than passing back a pointer to some internal memory.
BTW: I don't like code which passes around pointers to blocks of memory without also passing the size of that block. Seems a bit prone to nasty things.

Marshalling non-Blittable Structs from C# to C++

I'm in the process of rewriting an overengineered and unmaintainable chunk of my company's library code that interfaces between C# and C++. I've started looking into P/Invoke, but it seems like there's not much in the way of accessible help.
We're passing a struct that contains various parameters and settings down to unmanaged codes, so we're defining identical structs. We don't need to change any of those parameters on the C++ side, but we do need to access them after the P/Invoked function has returned.
My questions are:
What is the best way to pass strings? Some are short (device id's which can be set by us), and some are file paths (which may contain Asian characters)
Should I pass an IntPtr to the C# struct or should I just let the Marshaller take care of it by putting the struct type in the function signature?
Should I be worried about any non-pointer datatypes like bools or enums (in other, related structs)? We have the treat warnings as errors flag set in C++ so we can't use the Microsoft extension for enums to force a datatype.
Is P/Invoke actually the way to go? There was some Microsoft documentation about Implicit P/Invoke that said it was more type-safe and performant.
For reference, here is one of the pairs of structs I've written so far:
C++
/**
Struct used for marshalling Scan parameters from managed to unmanaged code.
*/
struct ScanParameters
{
LPSTR deviceID;
LPSTR spdClock;
LPSTR spdStartTrigger;
double spinRpm;
double startRadius;
double endRadius;
double trackSpacing;
UINT64 numTracks;
UINT32 nominalSampleCount;
double gainLimit;
double sampleRate;
double scanHeight;
LPWSTR qmoPath; //includes filename
LPWSTR qzpPath; //includes filename
};
C#
/// <summary>
/// Struct used for marshalling scan parameters between managed and unmanaged code.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ScanParameters
{
[MarshalAs(UnmanagedType.LPStr)]
public string deviceID;
[MarshalAs(UnmanagedType.LPStr)]
public string spdClock;
[MarshalAs(UnmanagedType.LPStr)]
public string spdStartTrigger;
public Double spinRpm;
public Double startRadius;
public Double endRadius;
public Double trackSpacing;
public UInt64 numTracks;
public UInt32 nominalSampleCount;
public Double gainLimit;
public Double sampleRate;
public Double scanHeight;
[MarshalAs(UnmanagedType.LPWStr)]
public string qmoPath;
[MarshalAs(UnmanagedType.LPWStr)]
public string qzpPath;
}
A blittable type is a type that has a common representation between managed and unmanaged code and can therefore be passed between them with little or no problem, e.g. byte, int32, etc.
A non-blittable type does not have the common representation, e.g. System.Array, System.String, System.Boolean, etc.
By specifying the MarshalAs attribute for a non-blittable type you can tell the marshaller what it should be converted to.
See this article on Blittable and Non-Blittable Types for more information
1 - What is the best way to pass strings? Some are short (device id's which can be set by us), and some are file paths (which may contain Asian characters)
StringBuilder is generally recommended as the easiest to use but I often use plain byte arrays.
2 - Should I pass an IntPtr to the C# struct or should I just let the Marshaller take care of it by putting the struct type in the function signature?
If the method is expecting a pointer then pass an IntPtr although you can get probably away with a ref in many cases depending on what it's going to be used for. If it's something that needs to stick around in the same place for a long time then I would manually allocate the memory with Marshal and pass the resulting IntPtr.
3 - Should I be worried about any non-pointer datatypes like bools or enums (in other, related structs)? We have the treat warnings as errors flag set in C++ so we can't use the Microsoft extension for enums to force a datatype.
Once you've got everything set up with the correct marshalling attributes I don't see why you'd need to worry. If in doubt put in the attribute, if the struct only ever gets used by managed code then the attribute won't be used.
4 - Is P/Invoke actually the way to go? There was some Microsoft documentation about Implicit P/Invoke that said it was more type-safe and performant.
Can't comment on this, you're into Visual C++ territory there.

Categories

Resources