I have this structure in C
struct system_info
{
const char *name;
const char *version;
const char *extensions;
bool path;
};
And this function signature
void info(struct system_info *info);
I'm trying to use this function like this:
[DllImport("...")]
unsafe public static extern void info(info *test);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public unsafe struct info
{
public char *name;
public char *version;
public char *extensions;
public bool path;
}
And on my main:
info x = new info();
info(&x);
I'm getting an error, pointers cannot reference to marshaled structures, how can I manage this?
There's no need at all to use unsafe here. I would do it like this:
public struct info
{
public IntPtr name;
public IntPtr version;
public IntPtr extensions;
public bool path;
}
And then the function is:
[DllImport("...")]
public static extern void getinfo(out info value);
You may need to specify the Cdecl calling convention, depending on the native code.
Call the function like this:
info value;
getinfo(out value);
string name = Marshal.PtrToStringAnsi(value.name);
// similarly for the other two strings fields
Since there is no mention of string length in the native code you posted, I'm assuming that the strings are allocated by the native code and that you don't need to to anything to deallocate them.
Solved by using ref instead of *test like Hans Passant mentioned
[DllImport("...")]
unsafe public static extern void info(ref system_info test);
Related
I would like to know what the difference is in using a ref return versus a pointer when doing pinvokes. The native method signature that I am calling is:
typedef struct Buffer {
const void* Data;
size_t Length;
} Buffer;
__declspec(dllexport) extern Buffer* GetNewBuffer();
On the C# end I have the Buffer struct mapped one to one.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Buffer
{
public IntPtr Data;
public UIntPtr Length;
}
I also have two static methods that call the native function implemented like so:
[DllImport("testlib", EntryPoint = "GetNewBuffer")]
public static extern unsafe Buffer* GetNewBufferPointer();
[DllImport("testlib", EntryPoint = "GetNewBuffer")]
public static extern ref Buffer GetNewBufferReference();
These two methods compile down to the following MSIL:
.method public hidebysig static pinvokeimpl("testlib" as "GetNewBuffer" winapi)
valuetype TestInterop.Buffer* GetBufferPointer () cil managed preservesig
{
} // end of method Native::GetBufferPointer
.method public hidebysig static pinvokeimpl("testlib" as "GetNewBuffer" winapi)
valuetype TestInterop.Buffer& GetBufferReference () cil managed preservesig
{
} // end of method Native::GetBufferReference
Using these methods and accessing the returned structs members would look something like this:
unsafe
{
Buffer* bufferPointer = Native.GetBufferPointer();
var dataFromPointer = bufferPointer->Data;
}
ref Buffer bufferReference = ref Native.GetBufferReference();
var dataFromReference = bufferReference.Data;
Is there any difference between these two methods besides the different syntax used to store the return value and access its members? When using ref return with pinvoke, does the GC do anything special that I should be aware of?
I have the following PInvoke:(C to C#)
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([MarshalAs(UnmanagedType.LPArray, SizeConst = 64)]sPiece[] board);
On C:
__declspec(dllexport) void InitBoard(sPiece board[8][8]);
In InitBoard function, the values of the matrix changing, but after a call to PInvoke I do not see the change.
sPiece[] board = new sPiece[64];
InitBoard(board);
//Here the values of the board is still initialized (as before the function call) at default values
I tried to change the variable to ref (although it already reference) but it stuck the program when the function was called, so I do not think it's the solution.
It took me a while to get here (I'm new to the subject) I'd love to help!
EDIT:
sPiece On C:
typedef struct Piece
{
ePieceType PieceType; //enum
ePlayer Player; //enum
int IsFirstMove;
} sPiece;
sPiece On C#:
[StructLayout(LayoutKind.Sequential)]
public struct sPiece
{
public ePieceType PieceType;
public ePlayer Player;
public int IsFirstMove;
}
Possibly you are failing to allocate memory before calling the function.
sPiece[] board = new sPiece[64];
InitBoard(board);
Declare the function like this:
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([Out] sPiece[] board);
The default marshalling is [In]. Although since your struct is blittable, the array you pass is pinned and the call behaves as though it was [In,Out]. So I think you could omit [Out] if you wished, but it is clearer as written above.
You can add the UnmanagedType.LPArray option if you wish but it's not needed.
int Set(CANMsg &CANObj)
I have to call the method above, from c#. Untill now i have defined a wrapper :
extern "C" __declspec(dllexport) int SetWrapper(CANMsg CANObj);
CANMsg CANObj --- is this parameter ok or should i use CANMsg *CANObj ?
and here i implement the wrapper:
extern "C" __declspec(dllexport) int SetWrapper(CANMsg CANObj)
{
return Set(CANObj);
}
I am creating this wrapper because this is an overloaded version of the function and i had to make a difference somehow.
Here is the CANMsg class:
class CANMsg
{
public:
CANMsg();
~CANMsg();
void AddRef() const;
void Release() const;
unsigned int MsgId;
unsigned int DLC;
unsigned int Handle;
unsigned int Interval;
unsigned int TimeStamp;
unsigned char Data0;
unsigned char Data1;
unsigned char Data2;
unsigned char Data3;
unsigned char Data4;
unsigned char Data5;
unsigned char Data6;
unsigned char Data7;
protected:
mutable int refCount;
};
Now, in C# i have the following :
[StructLayout(LayoutKind.Sequential)]
public class CANmsg
{
public int MsgId;
public int DLC;
public int Handle;
public int Interval;
public int TimeStamp;
public char Data0;
public char Data1;
public char Data2;
public char Data3;
public char Data4;
public char Data5;
public char Data6;
public char Data7;
}
and the import is like this :
[DllImport("engine.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
public static extern int SetWrapper(IntPtr canMSGObject);
I am a bit confused about that CANMsg object, am I declaring it ok as an IntPtr, is the marshal ok, or the types ? If i let it like so, with the IntPtr, what kind of instantiation should i perform there? If i send a CANMsg object, i get an error regarding some invalid arguments.
Let me know if you need some more details about this.
When I see your C++ class definition, I ask myself "what happens in the constructor and the destructor?" and "what do AddRef() and Release() do?" These are important questions because you can't simply project data from a C# object onto that IntPtr and hope for the best. Instead, you should think about making a helper dll that does this work for you. You might need methods something like this:
public ref class MyLibraryHelper {
public:
IntPtr MakeCANMsg() { return gcnew IntPtr(new CANMsg()); }
void DestroyCANMsg(IntPtr msgPtr) {
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (msg) delete msg;
}
void ProjectTo(CSharpCANMsg ^csh, IntPtr msgPtr)
{
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (!msg) return;
msg->MsgId = csh->get_MsgId();
// etc
}
void ProjectFrom(IntPtr msgPtr, CSharpCANMsg ^csh)
{
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (!msg) return;
csh->set_MsgId(msg->MsgId);
// etc
}
}
My C++/CLI is rusty, so expect some issues. If this looks like hand-marshalling, well, it is because given the class that you've exposed, it seems like you need it.
Now honestly, you probably don't want this. Really, you want a C++/CLI class that constructs a CANMsg and keeps it as a private member and then maps .NET properties onto the lower level object. This type of class will have to be disposable and the !ClassName() destructor will be responsible for deleting the underlying object.
You can not pass a C# object to native C++ like this. Marshal.StructureToPtr is what you need, the details and examples are here
I thought this one was fairly straight forward but still trying to understand all of this and having some issues.
I don't know much about the C function b/c i've been given limited information.
Here is the function call in C:
int GetCard(CardInfo card);
Here is the request structure:
typedef struct _tCardInfo
{
char CardNumber[80];
char isExist;
} TCardInfo, *pTCardInfo;
I want to pass the card number to see if it exists.
So in C# I did the following:
public struct CardInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string cardNumber;
public byte isExist;
}
[DllImport("card.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int GetCardInfo(ref CardInfo cardInfo);
Then in the c# method:
CardInfo cardInfo = new CardInfo();
cardInfo.cardNumber = "1234567890";
int success = GetCardInfo (ref cardInfo);
The good thing about the DLL that I'm calling is it generates a log file.
When I execute the call, the log tells me that I'm hitting the DLL but it is not passing the card number which then sets a message saying the card number was not passed.
Any help is greatly appreciated.
Thanks!
The problem is that you're requesting TChar marshaling, but the DLL requires 8-byte characters. Change the C struct to wchar_t.
Also, use Visual Studio to set a breakpoint in your DLL, and actually inspect the data when it comes in! Visual Studio can debug across .NET/native boundaries, which is super cool!
Try to add attribute StructLayout for struct
[StructLayout(LayoutKind.Sequential)]
public struct CardInfo
{
...
Try to create the .Net struct like this:
[StructLayout(LayoutKind.Sequential)]
public struct CardInfo
{
[MarshalAs(UnmanagedType.AnsiBStr, SizeConst = 80)]
public string cardNumber;
[MarshalAs(UnmanagedType.I1)]
public sbyte isExist;
}
And for the function declaration: try not to use the CallingConvention and CharSet in the DLL import, and use the [In, Out] attributes before the parameter. Like this:
[DllImport("card.dll")]
public static extern int GetCardInfo([In, Out] CardInfo cardInfo);
I am attempting to call a C++ function in a DLL from C# via interop. The DLL was written by someone else. The function signature is as follows:
AXI_DLL_EXPORT int __stdcall GetFileType(StringParam *stringParam, int *fileType)
StringParam is a struct defined as follows:
struct StringParam
{
int size; // 4 byte integer for the size of the string buffer
LPWSTR buff; // Pointer to a wide char buffer
}
This struct is used for passing strings back and forth. It can be used as both in and out parameters. The purpose of the size field in the struct is primarily for returning strings to the caller and to indicate the size of the buffer required. If the size of the buffer (provided by the caller) is too small to hold the string the caller can be informed of this and can then provide a larger buffer in a successive call to the function.
In C# I created a corresponding struct as follows:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct StringParam
{
public int Size;
[MarshalAs(UnmanagedType.LPWStr)]
public string Text;
public StringParam(string text)
{
this.Text = text;
this.Size = text.Length;
}
}
I declared the DllImport function call as follows:
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetFileType(StringParam stringParam, out int fileType);
The call fails with the following error:
A call to PInvoke function '... GetFileType' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
How should I declare the struct and call the native function from C#?
public static extern int GetFileType(ref StringParam stringParam, out int fileType);
Have you tried :
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetFileType(StringParam stringParam, ref int fileType);
And I think you have to pin the ref int before you pInvoke.