Pass multiple object arrays to C++ function from C# - c#

I am trying to execute a c++ function (32 bit) from c# code (console application).
C++ function signature:
EngError GUIFN eUpdateRecord(
Order::X * odrX, //array of objects
Order::Y * odrY, //array of objects
Order::P * odrP, //array of objects
Order::Q * odrQ, //single objects
ulong A,
ulong B,
int & C)
where Order is the C++ namespace containing the below classes:
class X
{
public:
pb::Uint XID; //Uint is unsigned short
pb::String No; //String is char *
pb::String Msg;
};
class Y
{
public:
pb::Uint YID;
pb::String Code;
pb::Uint OrderId;
pb::String Msg;
pb::Char Status;
pb::Long Capacity;
};
Classes P & Q are very similar to above classes with just change in property names (excluding them for now).
I am invoking the C++ function from C# as below
C# Method
[DllImport("x3iface.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public extern unsafe EngErr eUpdateRecord([In,Out] X[] odrX, [In, Out] Y[] odrY, [In, Out] P[] odrP, Q* odrQ, uint A, uint B, ref int C);
Sample C# Class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public unsafe struct Y
{
public UInt16 YID;
public char* Code;
public UInt16 OrderId;
public char* Msg;
public byte Status;
public Int32 Capacity;
}
All arrays passed to the C++ function have pre-assigned values and the C++ function will update values of each array object based on some internal logic.
When i run the code and step into the C++ function, the values are mapped correctly for arrays X and Y, but i am getting the error 'Unable to read memory' for the array P and object Q. All the other values A,B and C are mapped correctly.
If i swap the position of inputs X and Y with P and Q in C++( the parameter positions are changed accordingly in C# method too), then P and Q starts working fine, but then i get the error 'Unable to read memory' for the arrays X and Y.
public extern unsafe EngErr eUpdateRecord([In,Out] P[] odrX, Q* odrQ,[In, Out] X[] odrX, [In, Out] Y[] odrY, uint A, uint B, ref int C);
Do we have any limitation in the number of [In,Out] object arrays we can marshal and pass to a C++ function? How can i best trouble shoot this error?
I do not have a provision to alter the functionality in C++ code as it is being reused in other applications. Kindly suggest possible solutions.

Related

PInvoke - Attempted to read or write protected memory. This is often an indication that other memory is corrupt

I've seen the attempted to read or write protected memory error before.
Typically the error shows up when I don't set up the c# struct correctly. I do have other calls working properly but this one is not co-operating.
I'm almost certain that it could be both my function call and the struct that is causing the problem.
C Syntax
int CardTransaction(pTRequest req, char *ProductCodes)
Request structure (I condensed it b/c there were repetitive data types)
typedef struct _cardRequest
{
unsigned short RedemptionNum
long TotalAmount;
unsigned char filler1[257];
char CardNumber[80];
unsigned char cardType;
} TRequest, *pTRequest;
C# function call
[DllImport("card.dll"), CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int CardTransaction(ref CardRequest cardRequest, [MarshalAs(UnManagedType.LPStr)] StringBuilder productCodes);
ProductCodes is null so I just instantiated a stringbuilder object with nothing in it and passed it through. This is one place I'm thinking could be a problem.
C# structure
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct CardRequest
{
public uint16 RedemptionNum
public int TotalAmount;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string filler1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string CardNumber;
public byte cardType;
}
The obvious problem is that the C code uses an aligned struct, but for some reason you have elected to pack the C# struct. Remove the Pack = 1 from the C# code to make the two structures match.
Beyond that the filler array looks more like a byte array than a string. I'd declare it like this:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public byte[] filler1;
If you want to pass null to the productCodes parameter, then I expect you can do just that. I cannot recall every doing that myself, but generally when you pass null to a p/invoke, then the marshaller will pass NULL to the native code.

iterate over unknown c struct passed from c#

I am developing a c .dll that should be accessed from a C# program. Ideally, the .dll should receive any struct defined in C# and do something with it. So, initially, the struct type and size is unknown for the C dll. I was able to pass the struct through the C extern function, and it is supossed to be received alright, but, is there a way to find out the size and characteristics of this receive structure? is there a way to iterate over its members?
This is the C function defined for the dll
extern int __cdecl testCSharp(mystruct * Test){
//sizeof(Test) is 4, so it is ok
for(int i=0;i < sizeof(Test) ; i++){
char * value = (char*) Test; //This access the first element.
cout << value << endl; //Prints "some random string", so, it is received ok
}
return 1;
}
This is the C# code
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
unsafe public struct myStruct{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value1;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value2;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value3;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value4;
};
[DllImport("SMKcomUploadDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int testCSharp(ref myStruct message);
static void Main()
{
int result;
myStruct message = new myStruct();
message.value1 = "Some randome string";
message.value2 = "0";
message.value3 = "olkujhikvb";
message.value4 = "isabedfbfmlk";
result = testCSharp(ref message);
}
All the types are String in C#, and it is suppossed to remain like that, so it is the only thing I know about the struct that is going to be passed.
Any idea?
Thanks in advance
As you're marshalling them as ByValTStr with a length of 100, I'm not sure you'll be able to work any any more than what you already have (i.e. the first element).
From the MSDN (here)
.NET Framework ByValTStr types behave like C-style, fixed-size strings
inside a structure (for example, char s[5])
If you used LPStr or LPWStr null-termination instead you would be able to work out their lengths.

Marshalling C structures to C#

Suppose I have a structure:
typedef struct {
float x;
float y;
float z;
int ID;
} Vertex;
and a C++ function:
float first(Vertex* ptr, int length){ //really silly function, just an example
Vertex u,v;
u.x = ptr[0].x; //...and so on, copy x,y,z,ID
v.x = ptr[1].x;
return (u.x * v.x + u.y * v.y + u.z * v.z);
}
Vertex* another(float a, int desired_size){
Vertex v = (Vertex*)malloc(desired_size*sizeof(Vertex));
v[0].x = a;
v[1].x = -a; //..and so on.. make some Vertices.
return v;
}
First - my IDE. I'm using Visual Studio 2010, building a C# (4.0) application; The C++ part is also built in VS2010.
I know how to build a DLL of C/C++ code and use it in C# application, but until today I used only primitive arguments and return values like:
[DllImport("library.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern int simple(int a, int b);
Today I need to pass an array of structs (as in the example above).. and perhaps also receive one back..
How to I "translate" a C# class to a C struct (and vice versa) ??
The struct can be declared like this:
[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
public float x;
public float y;
public float z;
public int ID;
}
Next you need to settle on a calling convention. Your C++ code is almost certainly compiled with cdecl. Let's stick with that.
The function first is easy to call from C#:
[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern float first(Vertex[] vertices);
Note that you should not use SetLastError here–that's for Windows API functions. And there's no need to set the CharSet since there is no text here.
Now, for another things get more complex. If you can allocate the memory in the C# code then that is definitely the way to go.
void PopulateVertices(Vertex *vertices, int count)
{
for (int i=0; i<count; i++)
{
vertices[i].x = ....
}
}
On the C# side you declare it like this:
[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PopulateVertices(Vertex[] vertices, int count);
and call it like this
Vertex[] vertices = new Vertex[2];
PopulateVertices(vertices, vertices.Length);
If you don't want to allocate on the C# side of the fence then do it like this:
Return a pointer from the C++ code and allocate it with CoTaskMemAlloc.
In C# declare the return value of the imported function as IntPtr.
Use Marshal.PtrToStructure and some pointer arithmetic to marshal the return array into a C# array.
Call Marshal.FreeCoTaskMem to free the memory allocated in the native module.
But if you want my advice, try and stick to allocating the array in the managed code.
It should be this easy:
[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
float x;
float y;
float z;
int ID;
}
[DllImport("library.dll", CallingConvention=CallingConvention.StdCall)]
public static extern float first(Vertex[] verticies, int arrLen);
The issues you may run into would be if there is any packing done on the C version of the struct, and possibly the struct layout. If the layout doesn't match, you may want to change it to LayoutKind.Explicit and use the [FieldOffset(0)] attribute on each field. C would also have no idea, the length of the verticies array passed, so if that changes, you'd want to pass that along to the method.
To get an array back:
[DllImport("library.dll", CallingConvention=CallingConvention.StdCall)]
public static extern Vertex[] another(float a);
The marshaler handles all of the memory issues when passing arguments in, but returning the array it can't do anything. Since the memory is allocated on the unmanaged heap, the GC has no idea about it, and you will wind up with a memory leak. The marshaller will simply copy the native structs to the managed struct array, but it can't free the memory you've allocated with malloc.
The easiest way to get around it, if you can change the C++ code, would be to change the signature of another to take in an array of verticies (and the length of the array) instead of returning one. I don't need to write out any code for you that does this, #DavidHeffernan has already done this in his answer, the part of the break.

DllImport stuct parameters

I have 3 C++ structs as well as two methods and I want to use them via C#. Here is my C# code as well as few comments on the C++ code.
[StructLayout(LayoutKind.Sequential)]
public unsafe struct smat
{
public long rows;
public long cols;
public long vals;
public long* pointr;
public long* rowind;
public double* value;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct dmat {
long rows;
long cols;
double **value;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct svdrec {
int d;
dmat Ut; // it was dmat* in C++
double *S;
dmat Vt; // it was dmat* in C++
};
[DllImport(#"file.dll", EntryPoint = "svdNewSMat", CallingConvention = CallingConvention.Cdecl)]
public static extern smat svdNewSMat(int rows, int cols, int vals); // it was smat* in C++
[DllImport(#"file.dll", EntryPoint = "svdLAS2", CallingConvention = CallingConvention.Cdecl)]
public static extern svdrec svdLAS2(smat a, long dimensions, long iterations, double[] las2end, double kappa); // it was smat* and svdrec* in C++
EDIT:
Here are the C++ headers
typedef struct smat *SMat;
typedef struct dmat *DMat;
typedef struct svdrec *SVDRec;
/* Harwell-Boeing sparse matrix. */
struct smat {
long rows;
long cols;
long vals; /* Total non-zero entries. */
long *pointr; /* For each col (plus 1), index of first non-zero entry. */
long *rowind; /* For each nz entry, the row index. */
double *value; /* For each nz entry, the value. */
};
/* Row-major dense matrix. Rows are consecutive vectors. */
struct dmat {
long rows;
long cols;
double **value; /* Accessed by [row][col]. Free value[0] and value to free.*/
};
struct svdrec {
int d; /* Dimensionality (rank) */
DMat Ut; /* Transpose of left singular vectors. (d by m)
The vectors are the rows of Ut. */
double *S; /* Array of singular values. (length d) */
DMat Vt; /* Transpose of right singular vectors. (d by n)
The vectors are the rows of Vt. */
};
extern DMat svdNewDMat(int rows, int cols);
extern SVDRec svdLAS2(SMat A, long dimensions, long iterations, double end[2],
double kappa);
I am not giving the full code because It includes a lot of libraries.
And here is what I excecute to test:
var a = svdNewSMat(3, 6, 3);
var m = new double[] {1, 2};
var r = svdLAS2(a, 1, 0, m, 1e-6); // I get the Exception here
The second line gives me Attempted to read or write protected memory error.
Any ideas? Thanks in advance.
You cannot simply replace dmat* with dmat on the C# side. A pointer is not automatically marshalled as reference
Please look at http://msdn.microsoft.com/en-us/library/0szztey7(v=vs.80).aspx on how to marshall embedded pointers correctly.
If it was smat* and dmat*, use IntPtr instead smad/dmat.
You can just use IntPtr if you're working with pointers from the DLL. Keeping in mind that a lot of boxing/unboxing will occur during this work.
I don't believe a c++ module can perform operations on a c# struct.
Try using a class instead of a struct.
In function declarations, the pinvoke equivalent to pointer-to-pointer is usually ref IntPtr In a struct declaration (and as others pointed out), it should be just IntPtr. Keep in mind that in function invocation, things are often more complicated than just getting your struct declarations correct (e.g. calling conventions may need to be modified).

Converting Delphi code for unmanaged dll to C#

I hope someone can assist me with the problem I'm currently experiencing. We have a lot of Delphi legacy code, and need to convert some of our Delphi applications to C#.
The legacy code I'm currently struggling with is that of calling a function from a 3rd party application's non-COM DLL.
Here is the C-style header and struct used for the specific function:
/*** C Function AwdApiLookup ***/
extern BOOL APIENTRY AwdApiLookup( HWND hwndNotify, ULONG ulMsg,
BOOL fContainer, CHAR cObjectType,
SEARCH_CRITERIA* searchCriteria,
USHORT usCount, USHORT usSearchType,
VOID pReserved );
/*** C Struct SEARCH_CRITERIA ***/
typedef struct _search_criteria
{
UCHAR dataname[4];
UCHAR wildcard;
UCHAR comparator[2];
UCHAR datavalue[75];
} SEARCH_CRITERIA;
In our Delphi code, we have converted the above function and structure as:
(*** Delphi implementation of C Function AwdApiLookup ***)
function AwdApiLookup(hwndNotify: HWND; ulMsg: ULONG; fContainer: Boolean;
cObjectType: Char; pSearchCriteria: Pointer; usCount: USHORT;
usSearchType: USHORT; pReserved: Pointer): Boolean; stdcall;
external 'AWDAPI.dll';
(*** Delphi implementation of C Struct SEARCH_CRITERIA ***)
TSearch_Criteria = record
dataname: array [0..3] of char;
wildcard: char;
comparator: array [0..1] of char;
datavalue: array [0..74] of char;
end;
PSearch_Criteria = ^TSearch_Criteria;
and the way we call the above mentioned code in Delphi is:
AwdApiLookup(0, 0, true, searchType, #criteriaList_[0],
criteriaCount, AWD_USE_SQL, nil);
where criteriaList is defined as
criteriaList_: array of TSearch_Criteria;
After all that is said and done we can now look at the C# code, which I cannot get to work. I'm sure I'm doing something wrong here, or my C header is not translated correctly. My project does compile correctly, but when the function is called, I get a "FALSE" value back, which indicates that the function did not execute correctly in the DLL.
My C# code thus far:
/*** C# implementation of C Function AwdApiLookup ***/
DllImport("awdapi.dll", CharSet = CharSet.Auto)]
public static extern bool AwdApiLookup(IntPtr handle, ulong ulMsg,
bool fContainer, char cObjectType,
ref SearchCriteria pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
/*** C# implementation of C Struct SEARCH_CRITERIA ***/
[StructLayout(LayoutKind.Sequential)]
public struct SearchCriteria
{
private readonly byte[] m_DataName;
private readonly byte[] m_Wildcard;
private readonly byte[] m_Comparator;
private readonly byte[] m_DataValue;
public SearchCriteria(string dataName, string comparator, string dataValue)
{
m_DataName = Encoding.Unicode.GetBytes(
dataName.PadRight(4, ' ').Substring(0, 4));
m_Wildcard = Encoding.Unicode.GetBytes("0");
m_Comparator = Encoding.Unicode.GetBytes(
comparator.PadRight(2, ' ').Substring(0, 2));
m_DataValue = Encoding.Unicode.GetBytes(
dataValue.PadRight(75, ' ').Substring(0, 75));
}
public byte[] dataname { get { return m_DataName; } }
public byte[] wildcard { get { return m_Wildcard; } }
public byte[] comparator { get { return m_Comparator; } }
public byte[] datavalue { get { return m_DataValue; } }
}
My C# call to the C# function looks like this
var callResult = UnsafeAwdApi.CallAwdApiLookup(IntPtr.Zero, 0, true, 'W',
ref searchCriteria[0], criteriaCount,
66, null);
where searchCriteria and criteriaCount is defined as
List<SearchCriteria> criteriaList = new List<SearchCriteria>();
var searchCriteria = criteriaList.ToArray();
var criteriaCount = (ushort)searchCriteria.Length;
and adding data to searchCriteria:
public void AddSearchCriteria(string dataName, string comparator, string dataValue)
{
var criteria = new SearchCriteria();
criteria.DataName = dataName;
criteria.Wildcard = "0";
criteria.Comparator = comparator;
criteria.DataValue = dataValue;
criteriaList.Add(criteria);
}
Like I said, my code compiles correctly, but when the function executes, it returns "FALSE", which should not be the case as the Delphi function does return data with the exact same input.
I know I'm definitely doing something wrong here, and I've tried a couple of things, but nothing seems to be working.
Any assistance or nudge in the right direction would be greatly appreciated.
Thanks, Riaan
Several things here.
First of all C++ ULONG is a 32-bit integer, and becomes uint in C# - ulong is 64-bit.
For the struct, you don't need to mess with byte arrays. Use strings, and ByValTStr. Also, it's not really worth bothering with readonly and properties for interop structs. Yes, mutable value types are generally bad in a pure .NET API, but in this case it's the existing API, there's no point in masking it. So:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SearchCriteria
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4]
public string m_DataName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1]
public string m_Wildcard;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2]
public string m_Comparator;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 75]
public string m_DataValue;
}
If you really want to do all the string conversions yourself, it may be easier to just use unsafe and fixed-size arrays:
public unsafe struct SearchCriteria
{
public fixed byte m_DataName[4];
public byte m_Wildcard;
public fixed byte m_Comparator[2];
public fixed byte m_DataValue[75];
}
[EDIT] Two more things.
CHAR cObjectType should become byte cObjectType, and not char cObjectType that you currently use.
Also, yes, there is a problem with array marshaling in your example. Since your P/Invoke declaration is ref SearchCriteria pSearchCriteria - i.e. a single value passed by reference - that's precisely what P/Invoke mashaler will do. Keep in mind that, unless your struct only has fields of unmanaged types in it (the one with fixed arrays above is that, the one with string is not), the marshaler will have to copy the structs. It won't just pass address to the first element of the array directly. And in this case, since you didn't tell it it's an array there, it will only copy the single element you reference.
So, if you use the version of the struct with string, fields, you need to change the P/Invoke declaration. If you only need to pass SEARCH_CRITERIA objects into the function, but won't need to read data from them after it returns, just use an array:
public static extern bool AwdApiLookup(IntPtr handle, uint ulMsg,
bool fContainer, byte cObjectType,
SearchCriteria[] pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
And call it like this:
var callResult = UnsafeAwdApi.CallAwdApiLookup(
IntPtr.Zero, 0, true, (byte)'W',
searchCriteria, criteriaCount,
66, null);
If function writes data into that array, and you need to read it, use [In, Out]:
[In, Out] SearchCriteria[] pSearchCriteria,
If you use the version with fixed byte[] arrays, you can also change the P/Invoke declaration to read SearchCriteria* pSearchCriteria, and then use:
fixed (SearchCriteria* p = &searchCriteria[0])
{
AwdApiLookup(..., p, ...);
}
This will require unsafe as well, though.

Categories

Resources