How do I call this c function in C # (unmarshalling return struct)?

I want to use C # interop to call a function from a dll written in c. I have header files. Take a look at this:

enum CTMBeginTransactionError { CTM_BEGIN_TRX_SUCCESS = 0, CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS, CTM_BEGIN_TRX_ERROR_NOT_CONNECTED }; #pragma pack(push) #pragma pack(1) struct CTMBeginTransactionResult { char * szTransactionID; enum CTMBeginTransactionError error; }; struct CTMBeginTransactionResult ctm_begin_customer_transaction(const char * szTransactionID); 

How do I call ctm_begin_customer_transaction from C #. The char * constant is good for a string, but despite various attempts (looking at stackoverflow and other sites), I cannot marshal the return structure. If I define a function to return IntPtr, it works fine ...

Edit I changed the return type to IntPtr and used: CTMBeginTransactionResult structure = (CTMBeginTransactionResult) Marshal.PtrToStructure (ptr, typeof (CTMBeginTransactionResult)); but it throws an AccessViolationException

I also tried:

 IntPtr ptr = Transactions.ctm_begin_customer_transaction(""); int size = 50; byte[] byteArray = new byte[size]; Marshal.Copy(ptr, byteArray, 0, size); string stringData = Encoding.ASCII.GetString(byteArray); 

stringData == "70e3589b-2de0-4d1e-978d-55e22225be95 \ 0 \" \ 0 \ 0 \ a \ 0 \ 0 \ b \ b? "at this point." 70e3589b-2de0-4d1e-978d- 55e22225be95 "is the szTransactionID of the structure. Where is Enum? Is this the next byte?

+8
c # interop pinvoke unmarshalling
source share
2 answers

I do not like to answer my question, but I found a solution for marshaling the resulting structure. The structure is 8 bytes long (4 bytes for char * and 4 bytes for enumeration). String marching does not work automatically, but the following works:

 // Native (unmanaged) public enum CTMBeginTransactionError { CTM_BEGIN_TRX_SUCCESS = 0, CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS, CTM_BEGIN_TRX_ERROR_NOT_CONNECTED }; // Native (unmanaged) [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] internal struct CTMBeginTransactionResult { public IntPtr szTransactionID; public CTMBeginTransactionError error; }; // Managed wrapper around native struct public class BeginTransactionResult { public string TransactionID; public CTMBeginTransactionError Error; internal BeginTransactionResult(CTMBeginTransactionResult nativeStruct) { // Manually marshal the string if (nativeStruct.szTransactionID == IntPtr.Zero) this.TransactionID = ""; else this.TransactionID = Marshal.PtrToStringAnsi(nativeStruct.szTransactionID); this.Error = nativeStruct.error; } } [DllImport("libctmclient-0.dll")] internal static extern CTMBeginTransactionResult ctm_begin_customer_transaction(string ptr); public static BeginTransactionResult BeginCustomerTransaction(string transactionId) { CTMBeginTransactionResult nativeResult = Transactions.ctm_begin_customer_transaction(transactionId); return new BeginTransactionResult(nativeResult); } 

The code works, but I still need to investigate if calling unmanaged code leads to memory leaks.

+1
source share

There is a memory management problem in this structure. Who owns the C string pointer? The pinvoke router will always assume that the caller owns it, so it will try to issue a string. And passes a pointer to CoTaskMemFree (), the same function as the pointer Marshal.FreeCoTaskMem (). These features use the COM memory allocator, the universal interop memory manager on Windows.

This rarely comes to a good end; C code usually does not use this allocator unless the programmer has designed its code to allow for interaction. In this case, he would never use the structure as a return value, interop always works much less flawlessly when the calling server supplies buffers.

Thus, you cannot allow the marshaller to do his normal duty. You must declare the return type as IntPtr so that it does not try to release a string. And you have to marshal it yourself using Marshal.PtrToStructure ().

This still leaves the question unanswered, who owns the string? You cannot do anything to free the string buffer, you do not have access to the allocator used in the C code. The only hope that you have is that the string was not actually allocated on the heap. Perhaps C can use string literals. You have to check it out. Call a function a billion times in a test program. If this does not blow up the program, then you are kind. If not, then only C ++ / CLI can solve your problem. Given the nature of the string, the "transaction ID" should change a lot, I would say that you have a problem.

+5
source share

All Articles