2013-03-08 2 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); 

가 어떻게 # C에서 ctm_begin_customer_transaction를 호출 않습니다. const char * 잘 문자열, mapps 있지만 다양한 시도 (stackoverflow 및 기타 사이트보고)에도 불구하고 반환 구조를 마샬링 할 실패합니다. CTMBeginTransactionResult 구조 = (CTMBeginTransactionResult) Marshal.PtrToStructure (PTR의 typeof (CTMBeginTransactionResult)) : 내가 편집 이 작품을 좋아 INTPTR 반환하는 기능 ...

를 정의하면 내가 IntPtr입니다 및 사용에 반환 유형을 변경 한 ;

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 \ 0 \ 0 \ B \ \ 는하지만 I는 시도 AccessViolationException

을 던진다 이 시점에서 "b"? "70e3589b-2de0-4d1e-978d-55e22225be95"는 struct의 szTransactionID입니다. Enum은 어디에 있습니까? 다음 바이트입니까?

답변

1

나는 내 자신의 질문에 답하기를 싫지만 결과 구조체를 마샬링하는 해결책을 찾았습니다. struct는 8 바이트 길이입니다 (char *에는 4 바이트, enum에는 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 문자열 포인터를 소유합니까? pinvoke marshaller는 호출자가 문자열을 해제하려고 시도 할 수 있도록 항상 호출자가 소유하고 있다고 가정하고 포인터를 CoTaskMemFree()에 전달합니다. 그는 Marshal.FreeCoTaskMem()에 의해 호출 된 하나. 이러한 함수는 Windows의 범용 interop 메모리 관리자 인 COM 메모리 할당자를 사용합니다.

프로그래머가 interop을 염두에두고 코드를 설계하지 않으면 C 코드는 일반적으로 해당 할당자를 사용하지 않습니다. 어떤 경우에는 구조체를 반환 값으로 사용하지 않았으며 호출자가 버퍼를 제공 할 때 interop은 항상 문제없이 작동합니다.

따라서 마샬 러가 정상적인 의무를 수행하도록 허용 할 여유가 없습니다. 반환 값 형식을 IntPtr로 선언해야만 문자열을 해제하려고 시도하지 않습니다. 그리고 당신은 마샬에게 직접 마샬링해야합니다 .PtrToStructure().

그래도 질문에 답변을하지 않으면 누가 문자열을 소유합니까? 문자열 버퍼를 해제 할 수있는 방법이 없으며 C 코드에서 사용되는 할당 자에 액세스 할 수 없습니다. 유일한 희망은 문자열이 실제로 힙에 할당되지 않았다는 것입니다. C 프로그램이 문자열 리터럴을 사용하고있을 수도 있습니다. 추측을 확인해야합니다. 테스트 프로그램에서 함수를 수십억 번 호출하십시오. 그것이 프로그램을 폭발시키지 않는다면 당신은 훌륭합니다. 그렇지 않다면 C++/CLI만이 문제를 해결할 수 있습니다. 문자열의 본질을 감안할 때 "거래 ID"가 많이 바뀌어야합니다. 문제가 있다고 말하고 싶습니다.

+0

답장을 보내 주셔서 감사합니다. 덧글은 내 대답을 놓을 수있는 나쁜 곳이므로 질문을 대신 편집했습니다. – Eiver

+0

잠재적 인 메모리 누수 문제를 발견하기위한 +1 – Eiver

관련 문제