2009-09-30 3 views
5

ich eine C-DLL-Funktion aufrufen und müssen die folgende C-Struktur liefern:Rangierung Array von Strings auf char ** in 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; 

Das Problem mit char ** mFieldNames; Ich habe automatisch wie folgt versucht Rangier:

[MarshalAs (UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPTStr, SizeConst = 9)] public String [] mFieldNames;

Auf diese Weise erhalte ich einen Fehler in Marshal.SizeOf() - kann nicht die richtige Größe berechnen. Dann entschied ich mich, manuell mit Zeigern umzugehen. Es ist in der Tat nur ein Zeiger auf das Array von C-Strings. Hier ist mein Code, der zu

System.AccessViolationException führt: Versuch, geschützten Speicher zu lesen oder zu schreiben. Dies ist oft ein Hinweis darauf, dass anderer Speicher beschädigt ist.

Also habe ich irgendwo Zeiger vermasselt. Der Code scheint mir OK, wo ist der Fehler?

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

Update: es bricht in sakeSearchForRecords(); – Slawa

Antwort

7

Der beste Weg, böse Zeichenfolge Zeiger, vor allem Doppel Zeiger auf Marschall in einer Struktur einfach ist, um einen IntPtr zu verwenden.

public IntPtr mFieldNames; 

Dies wird Marshal korrekt, wenn auch mit einem nicht so nützlichen Typ. Wenn Sie jedoch die Struktur von IntPtr verstehen, ist es sehr einfach, die resultierenden Strings zu entfernen.

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

Der einzige wirkliche Nachteil ist, dass Sie manuell die Speicher

+0

Wenn ich versuchen würde, Ihre Funktion zu verwenden (ich habe eine ähnliche, wenn Sie meinen Code ansehen (mit Array)), wie bekomme ich IntPtr aus der "Liste "? IntPtr pList = &list; // ?? – Slawa

1

Eine bessere Möglichkeit haben zu befreien ist einfach unsicheren Code mit sbyte zu verwenden, die die gleichen wie c-Zeichen ist (-128 bis 127) 1 Byte. Sie können einige externe Funktionen wie alloc_txt, free_txt, etc .. zum Zuweisen und Freigeben von dem Heap schreiben. Wenn ich mit Interop schreibe, verwende ich meist unsicheren Code, weil IntPtr dir die Adresse liefert, aber du musst externe Funktionen verwenden, um Member in der Struktur zu erhalten oder wenn ein Primitiv Marshal-Methoden verwenden muss, um den Wert zu extrahieren.

Das einzige Mal, dass Sie eine C# -Struktur als unsicher deklarieren müssen, ist, wenn Sie tatsächliche Zeiger verwenden, die Sie nicht verwenden, sondern stattdessen MarshalAs verwenden. Ich würde immer noch bevorzugen, dass Sie unsichere Zeiger über MarshalAs (UnmanagedType.?) Verwenden, mit denen Sie direkt mit den Mitgliedern umgehen können.

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