2009-09-30 47 views
5

我調用C DLL函數,需要提供以下C結構:編組陣列爲char **在C#

typedef struct 
{ 
    char  *mTableId; 
    char  **mFieldNames; 
    int  mNumFields; 
    char  *mFilter; 
    char  *mSort; 
    int  mOffset; 
    int  mMaxRecords; 
    char  *mTargetRecordFilter; 
    int  mSurroundingRecordsCount; 
    int  *mOwnerIds; 
    int  mNumOwnerIds; 
    gsi_bool mCacheFlag; 
} SAKESearchForRecordsInput; 

的問題是使用char ** mFieldNames;我已經試過自動編組這樣的:

[的MarshalAs(UnmanagedType.LPArray,ArraySubType = UnmanagedType.LPTStr,SizeConst = 9)] 公共字符串[] mFieldNames;

這樣我在Marshal.SizeOf()中得到一個錯誤 - 無法計算正確的大小。然後我決定手動處理指針。它實際上只是一個指向C字符串數組的指針。這是我的代碼,這是導致

System.AccessViolationException:試圖讀取或寫入受保護的內存。這通常表明其他內存已損壞。

所以我在什麼地方搞砸了指針。代碼對我來說似乎還行,bug在哪裏?

C#:

[StructLayout(LayoutKind.Sequential)] 
unsafe public class SAKESearchForRecordsInput { 
    [MarshalAs(UnmanagedType.LPTStr)] 
    public String mTableId; 
    //[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPTStr, SizeConst = 9)] // HARDCODED!?! 
    //public String[] mFieldNames;  // char  **mFieldNames; 
    public IntPtr mFieldNames; 
    public int mNumFields; 
    [MarshalAs(UnmanagedType.LPTStr)] 
    public String mFilter; 
    [MarshalAs(UnmanagedType.LPTStr)] 
    public String mSort; 
    public int mOffset; 
    public int mMaxRecords; 
    //[MarshalAs(UnmanagedType.LPTStr)] 
    public IntPtr mTargetRecordFilter; 
    public int mSurroundingRecordsCount; 
    public IntPtr mOwnerIds; 
    public int mNumOwnerIds; 
    public gsi_bool mCacheFlag; 
} 

    [DllImport("saketestd.dll")] 
    unsafe static extern void* sakeSearchForRecords(
    IntPtr sake, 
    IntPtr input, //SAKESearchForRecordsInput * 
    SAKERequestCallback callback, //SAKERequestCallback 
    IntPtr userData); 

    unsafe public bool sakeSearchForRecordsE() { 
    bool ret = false; 
    try { 
    searchInput.mTableId = "bbdx_score"; 
    //searchInput.mFieldNames = mFieldNames.to; 
    searchInput.mFilter = "num_ratings = 0 AND filestore > 0"; 
    searchInput.mSort = ""; 
    searchInput.mOffset = 0; 
    searchInput.mMaxRecords = 1; 
    //searchInput.mTargetRecordFilter = ""; 
    searchInput.mSurroundingRecordsCount = 0; 
    searchInput.mOwnerIds = IntPtr.Zero; 
    searchInput.mNumOwnerIds = 0; 
    searchInput.mCacheFlag = true; 

    int sakeSize = Marshal.SizeOf(sake); 
    debug.AddLine(this.getMethodName() + ": sizeof(sake): " + sakeSize); 
    IntPtr pSake = Marshal.AllocHGlobal(sakeSize); 
    Marshal.StructureToPtr(sake, pSake, true); 

    int inputSize = Marshal.SizeOf(searchInput); 
    debug.AddLine(this.getMethodName() + ": sizeof(input): " + inputSize); 
    IntPtr pInput = Marshal.AllocHGlobal(inputSize); 
    Marshal.StructureToPtr(searchInput, pInput, true); 

    IntPtr[] mFieldNamesPtr; 
    int i; 
    if (true) { // IntPtr[] 
    mFieldNamesPtr = new IntPtr[mFieldNames.Length]; 
    i = 0; 
    foreach (string str in mFieldNames) { 
     mFieldNamesPtr[i++] = Marshal.StringToHGlobalAnsi(str); 
    } 
    //searchInput.mFieldNames = mFieldNamesPtr; 
    } else { 
    //searchInput.mFieldNames = mFieldNames; 
    } 
    searchInput.mNumFields = mFieldNames.Length; 

    void* pRequestInternal = null; 
    void* p = mFieldNamesPtr[0].ToPointer(); 
    searchInput.mFieldNames = (IntPtr)p; 
    pRequestInternal = sakeSearchForRecords(
     pSake, 
     pInput, 
     new SAKERequestCallback(this.sakeSearchForRecordsCB), 
     IntPtr.Zero 
    ); 


    sake = (SAKEInternal)Marshal.PtrToStructure(pSake, typeof(SAKEInternal)); 
    if (searchRequest == null) { 
    debug.AddLine(this.getMethodName() + ": mStartRequestResult: " + sake.mStartRequestResult); 
    } else { 
    ret = true; 
    this.searchRequest = (SAKERequestInternal)Marshal.PtrToStructure(
     new IntPtr(pRequestInternal), 
     typeof(SAKERequestInternal) 
    ); 
    searchInput = (SAKESearchForRecordsInput)Marshal.PtrToStructure(
     pInput, 
     typeof(SAKESearchForRecordsInput) 
    ); 

    if (true) { 
     i = 0; 
     foreach (string str in mFieldNames) { 
     Marshal.FreeHGlobal(mFieldNamesPtr[i++]); 
     } 
    } 

    PrintStruct ps = new PrintStruct(sake); 
    debug.AddLine(this.getMethodName() + ": sake: " + ps); 
    ps = new PrintStruct(searchRequest); 
    debug.AddLine(this.getMethodName() + ": searchRequest: " + ps.print_r()); 
    ps = new PrintStruct(searchInput); 
    debug.AddLine(this.getMethodName() + ": searchInput: " + ps.print_r()); 
    } 
    Marshal.FreeHGlobal(pSake); 
    Marshal.FreeHGlobal(pInput); 
    } catch (Exception ex) { 
    debug.Text += ex.ToString(); 
    } 
    return ret; 
    } 
+0

更新:它在sakeSearchForRecords(); – Slawa 2009-09-30 15:37:56

回答

7

元帥討厭的字符串指針,特別是雙指針內的結構是簡單地用一個IntPtr的最好方法。

public IntPtr mFieldNames; 

這將盡管使用不太有用的類型,但仍會正確。但是,如果您瞭解IntPtr的結構,則很容易得到結果字符串。

public static List<string> GetAllStrings(IntPtr ptr, int size) { 
    var list = new List<string>(); 
    for (int i = 0; i < size; i++) { 
    var strPtr = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr)); 
    list.Add(Marshal.PtrToStringUni(strPtr)); 
    ptr = new IntPtr(ptr.ToInt64()+IntPtr.Size); 
    } 
    return list; 
} 

唯一真正的缺點是,你必須手動釋放內存

+0

如果我會嘗試使用你的函數(如果你看我的代碼(使用數組),我有類似的),我怎麼能從「列表」獲得IntPtr? IntPtr的PLIST = &list; // ?? – Slawa 2009-10-05 16:10:43

1

更好的方法是簡單地使用具有爲sbyte不安全的代碼是一樣的C-CHAR(-128〜127) 1個字節。 你可以寫自己喜歡alloc_txt,free_txt等一些extern函數..分配和堆釋放。大多數時候,我與互操作寫我使用不安全的代碼,因爲IntPtr的讓你的地址,但你仍然必須使用extern函數來獲得成員它指向或者如果原始的結構必須元帥方法提取的價值是什麼。

必須聲明一個C#結構不安全的唯一情況是,如果你使用的是哪個你沒有,但使用的MarshalAs而不是實際的指針。我還是寧願使用通過的MarshalAs不安全的指針(UnmanagedType?),它允許你直接處理的成員。

[Struct(Layout.Sequential)] 
public unsafe struct SAKESearchForRecordsInput 
{ 
sbyte*mTableId; 
sbyte**mFieldNames; 
int mNumFields; 
sbyte*mFilter; 
sbyte*mSort; 
int mOffset; 
int mMaxRecords; 
char*mTargetRecordFilter; 
int mSurroundingRecordsCount; 
int*mOwnerIds; 
int mNumOwnerIds; 
bool mCacheFlag;//?don't know what the typedef for the bytes 
};