2013-03-08 43 views
8

我想用c#interop從c中編寫的dll調用一個函數。我有頭文件。 看看這個:如何在c#中調用此c函數(解組返回結構)?

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); 

如何調用ctm_begin_customer_transaction從C#。 const char *很好地適用於字符串,但儘管有各種嘗試(查看stackoverflow和其他站點),但我無法編組返回結構。如果我定義返回IntPtr的它工作正常功能...

編輯 我改變了返回類型的IntPtr和用途: CTMBeginTransactionResult結構=(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 \在這個結構中,「70e3589b-2de0-4d1e-978d-55e22225be95」是來自結構的szTransactionID,其中是Enum?它是下一個字節嗎?

回答

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); 
} 

代碼工作,但我仍然需要進行調查,如果調用內存泄漏的非託管代碼的結果。

5

這個結構中隱藏了內存管理問題誰擁有C字符串指針?編譯器總是會假設調用者擁有它,所以它會嘗試釋放字符串並將指針傳遞給CoTaskMemFree(),其功能與t他由Marshal.FreeCoTaskMem()調用。這些函數使用COM內存分配器,即Windows中的通用互操作內存管理器。

這很少會達到一個好的結果,C代碼通常不會使用該分配器,除非程序員在設計interop時考慮了代碼。在這種情況下,他從來沒有使用過一個結構體作爲返回值,當調用者提供緩衝區時,互操作性總是不那麼麻煩。因此你不能讓編組履行其正常職責。您必須將返回值類型聲明爲IntPtr,因此它不會嘗試釋放該字符串。你必須用Marshal.PtrToStructure()自己編組它。

但是仍然沒有答案,誰擁有字符串?沒有辦法釋放字符串緩衝區,您無法訪問C代碼中使用的分配器。你唯一的希望是字符串並沒有真正分配在堆上。這是可能的,C程序可能使用字符串文字。你需要驗證猜測。在測試程序中調用十億次函數。如果這不會導致程序爆炸,那麼你很好。如果沒有,那麼只有C++/CLI可以解決你的問題。鑑於字符串的性質,「交易ID」應該改變很多,我會說你確實有問題。

+0

謝謝你的回答。評論是放置我的答案的不好的地方,所以編輯這個問題。 – Eiver 2013-03-08 13:39:04

+0

+1用於發現潛在的內存泄漏問題 – Eiver 2013-03-11 13:38:03