I have the following struct (On C) :
typedef struct Piece
{
ePieceType PieceType;//enum
ePlayer Player;//enum
int IsFirstMove;
} sPiece;
(On C#):
[StructLayout(LayoutKind.Sequential)]
public struct Piece
{
public PieceTypeEnum PieceType; //same enum order like ePieceType
public PlayerEnum Player; //same enum order like ePlayer
public int IsFirstMove;
}
I have the following function that I want to export :
__declspec(dllexport) void InitBoard(sPiece board[8][8]);
C#:
[DllImport("chess_api.dll", SetLastError = true)]
static extern void InitBoard([MarshalAs(UnmanagedType.SafeArray)]Piece[][] board);
I get the following error:
I think I didn't marshal the array as well, Can you help me?
The answer is:
Just declare it Piece [] and pass an array with 64 elements
Posted in comments by Hans Passant(but somehow did not raise it as an answer).
Related
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.
I am trying to access and make changes to elements in a struct that are in a DLL. I have followed this example on how to use struct from a DLL: http://nawatt.com/index.php/corporate/blog/78-using-c-dlls-in-c
I have been successful in making changes to non-array variables, but whenever I try to make changes to an array I get a Runtime Error.
This is an example of my C DLL code:
//lib_qrs.dll
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TEST_STRUCT
{
unsigned short check[5];
} test_struct;
__declspec(dllexport) void __stdcall test(test_struct *test, unsigned short val){
// This is an example of what DOES NOT WORK
test->check[0]=val+1;
test->check[1]=val+2;
test->check[2]=val+3;
test->check[3]=val+4;
test->check[4]=val+5;
}
#ifdef __cplusplus
}
#endif
This is an example of my C# code:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT
{
public UInt16[] check;
}
public class Program
{
[DllImport("lib_qrs.dll", EntryPoint="test", CallingConvention = CallingConvention.StdCall)]
public static extern void test(ref TEST_STRUCT test, int val);
public TEST_STRUCT testStruct = new TEST_STRUCT();
static void Main(string[] args)
{
testStruct.check=new UInt16[5];
// WHERE ERROR OCCURS
test(ref testStruct, 5);
}
}
The error that I get is:
*"An unhandled exception of type 'System.AccessViolationException' occurred in Test.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I understand that I have to be very careful with memory allocation when I replicate my structure in C#, but I don't know what I am doing wrong or how I can fix this array issue.
Does anyone have an idea of how I could get around this?
The default marshaling for that array is unsigned short*, not unsigned short[]. You'll need to apply the [MarshalAs] attribute to tell the pinvoke marshaller about it. Fix:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public UInt16[] check;
}
And since you are returning values in the array, you also have to tell the pinvoke marshaller that it needs to copy the array values back. That requires the [Out] attribute:
[DllImport("lib_qrs.dll")]
public static extern void test([Out] ref TEST_STRUCT test, int val);
Do note that neither is necessary at all if the you just declare the argument as ushort[], assuming that the structure doesn't contain any additional fields.
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);
this is just an example
I have a c++ struct like this:
typedef struct _cdm_status {
BYTE error_cd;
BYTE sensor[9];
} SDMSTATUS, *LPSDMSTATUS;
what would be the c# equivalent?
and how can I use it in this example?
[DllImport("MFSSDM.dll")]
public static extern bool MFSCommSDM_Status(LPSDMSTATUS lpStatus);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct _cdm_status {
public Byte error_cd;
public fixed Byte sensor[9];
}
public class _cdm_status
{
public byte error_cd;
public byte[] sensor = new byte[9];
}
C# doesn't have typedefs, so instead of referring to the type as "SDMSTATUS", you would just use the actual type name "_cdm_status".
It seems that you are trying wanting to convert the struct so that you can pass it to a native function using p/invoke. I would do that like so:
public struct SDMSTATUS
{
public byte error_cd;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=9)]
public byte sensor[];
}
Then to declare the function you must make sure that a pointer to the struct is passed. So declare the p/invoke like this:
[DllImport("MFSSDM.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern bool MFSCommSDM_Status(ref SDMSTATUS Status);
I've assumed that the calling convention is cdecl, but you need to substitute the true value.
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);