我想使用c#interop从用c编写的dll调用函数。我有头文件。 看看这个:
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);
如何从c#调用ctm_begin_customer_transaction。 const char * mapps很好地用于字符串,但是尽管有各种尝试(查看stackoverflow和其他站点),但我无法编组返回结构。如果我定义函数返回IntPtr它可以正常工作......
修改的 我将返回类型更改为IntPtr并使用: CTMBeginTransactionResult structure =(CTMBeginTransactionResult)Marshal.PtrToStructure(ptr,typeof(CTMBeginTransactionResult)); 但它会抛出AccessViolationException
我也尝试过:
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?“此时。”70e3589b-2de0-4d1e-978d- 55e22225be95“是结构中的szTransactionID。枚举在哪里?它是下一个字节吗?
答案 0 :(得分:5)
此结构中隐藏了内存管理问题。谁拥有C字符串指针? pinvoke marshaller将始终假定调用者拥有它,因此它将尝试释放字符串。并将指针传递给CoTaskMemFree(),与Marshal.FreeCoTaskMem()调用的函数相同。这些函数使用COM内存分配器,Windows中的通用互操作内存管理器。
这很少有好结果,C代码通常不会使用该分配器,除非程序员设计了他的代码并考虑到了interop。在这种情况下,他从来没有使用过结构作为返回值,当调用者提供缓冲区时,互操作总是更加无故障。
所以你不能让编组人员履行其正常职责。您必须将返回值类型声明为IntPtr,以便它不会尝试释放字符串。你必须自己用Marshal.PtrToStructure()编组。
然而,这仍然没有答案,谁拥有字符串?您无法释放字符串缓冲区,也无法访问C代码中使用的分配器。你唯一的希望是字符串实际上没有在堆上分配。这是可能的,C程序可能正在使用字符串文字。你需要验证猜测。在测试程序中调用该函数十亿次。如果这没有爆炸程序,那么你很好。如果没有,那么只有C ++ / CLI可以解决您的问题。鉴于字符串的性质,“交易ID”应该改变很多,我会说你确实有问题。
答案 1 :(得分:1)
我讨厌回答我自己的问题,但是我找到了解决方案来编组生成的结构。结构长度为8个字节(char *为4个字节,枚举为4个字节)。编组字符串不会自动运行,但以下工作:
// 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);
}
代码有效,但我仍需要调查,如果调用非托管代码导致内存泄漏。