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