2014-05-01 5 views
6

Aufbauend von meinem marshalling helloworld question, ich renne Probleme in Marshalling ein Array in C bis C# zugeordnet. Ich habe Stunden damit verbracht zu recherchieren, wo ich vielleicht falsch liege, aber alles, was ich versucht habe, endet mit Fehlern wie AccessViolationException.Marshalling C-Array in C# - Einfach HelloWorld

Die Funktion, die das Erstellen eines Arrays in C behandelt, ist unten.

__declspec(dllexport) int __cdecl import_csv(char *path, struct human ***persons, int *numPersons) 
{ 
    int res; 
    FILE *csv; 
    char line[1024]; 
    struct human **humans; 

    csv = fopen(path, "r"); 
    if (csv == NULL) { 
     return errno; 
    } 

    *numPersons = 0; // init to sane value 
    /* 
    * All I'm trying to do for now is get more than one working. 
    * Starting with 2 seems reasonable. My test CSV file only has 2 lines. 
    */ 
    humans = calloc(2, sizeof(struct human *)); 
    if (humans == NULL) 
     return ENOMEM; 

    while (fgets(line, 1024, csv)) { 
     char *tmp = strdup(line); 
     struct human *person; 

     humans[*numPersons] = calloc(1, sizeof(*person)); 
     person = humans[*numPersons]; // easier to work with 
     if (person == NULL) { 
      return ENOMEM; 
     } 
     person->contact = calloc(1, sizeof(*(person->contact))); 
     if (person->contact == NULL) { 
      return ENOMEM; 
     } 

     res = parse_human(line, person); 
     if (res != 0) { 
      return res; 
     } 

     (*numPersons)++; 
    } 
    (*persons) = humans; 

    fclose(csv); 

    return 0; 
} 

Der C# -Code:

IntPtr humansPtr = IntPtr.Zero; 
int numHumans = 0; 

HelloLibrary.import_csv(args[0], ref humansPtr, ref numHumans); 

HelloLibrary.human[] humans = new HelloLibrary.human[numHumans]; 
IntPtr[] ptrs = new IntPtr[numHumans]; 
IntPtr aIndex = (IntPtr)Marshal.PtrToStructure(humansPtr, typeof(IntPtr)); 

// Populate the array of IntPtr 
for (int i = 0; i < numHumans; i++) 
{ 
    ptrs[i] = new IntPtr(aIndex.ToInt64() + 
      (Marshal.SizeOf(typeof(IntPtr)) * i)); 
} 

// Marshal the array of human structs 
for (int i = 0; i < numHumans; i++) 
{ 
    humans[i] = (HelloLibrary.human)Marshal.PtrToStructure(
     ptrs[i], 
     typeof(HelloLibrary.human)); 
} 

// Use the marshalled data 
foreach (HelloLibrary.human human in humans) 
{ 
    Console.WriteLine("first:'{0}'", human.first); 
    Console.WriteLine("last:'{0}'", human.last); 

    HelloLibrary.contact_info contact = (HelloLibrary.contact_info)Marshal. 
     PtrToStructure(human.contact, typeof(HelloLibrary.contact_info)); 

    Console.WriteLine("cell:'{0}'", contact.cell); 
    Console.WriteLine("home:'{0}'", contact.home); 
} 

Die erste human struct wird vermarshallten in Ordnung. Ich erhalte die Zugriffsverletzungsausnahmen nach dem ersten. Ich habe das Gefühl, dass ich etwas vermisse mit Marshalling-Strukturen mit Struktur-Zeigern in ihnen. Ich hoffe, ich habe einen einfachen Fehler, den ich übersehen habe. Siehst du etwas falsch mit diesem Code?

Siehe diese GitHub gist für die vollständige Quelle.

Antwort

2
// Populate the array of IntPtr 

Hier ging es schief. Sie erhalten einen Zeiger auf ein Array von Zeigern zurück. Sie haben die erste richtig gelesen und tatsächlich den Zeigerwert aus dem Array gelesen. Aber dann hat deine for() - Schleife es falsch gemacht, indem du nur 4 (oder 8) zum ersten Zeigerwert hinzugefügt hast. Anstatt sie aus dem Array zu lesen. Fix:

IntPtr[] ptrs = new IntPtr[numHumans]; 

    // Populate the array of IntPtr 
    for (int i = 0; i < numHumans; i++) 
    { 
     ptrs[i] = (IntPtr)Marshal.PtrToStructure(humansPtr, typeof(IntPtr)); 
     humansPtr = new IntPtr(humansPtr.ToInt64() + IntPtr.Size); 
    } 

Oder viel sauberer, da Arrays von einfachen Typen Marshalling wird bereits unterstützt:

IntPtr[] ptrs = new IntPtr[numHumans]; 
    Marshal.Copy(humansPtr, ptrs, 0, numHumans); 

ich den Fehler gefunden, indem Sie den Debug + Windows-+ Speicher + Speicher mit 1 Put humansPtr Im Adressfeld wurde auf 4-Byte-Integer-Ansicht umgeschaltet und beobachtet, dass der C-Code es richtig gemacht hat. Dann fand ich schnell heraus, dass ptrs [] nicht die Werte enthielt, die ich im Speicherfenster sah.

Nicht sicher, warum Sie Code so schreiben, anders als als eine geistige Übung. Es ist nicht der richtige Weg, dies zu tun, Sie ignorieren zum Beispiel völlig die Notwendigkeit, den Speicher wieder freizugeben. Welches ist sehr nicht trivial. Das Analysieren von CSV-Dateien in C# ist ziemlich einfach und genauso schnell wie in C, es ist I/O-gebunden, nicht ausführungsgebunden. Sie werden diese fast unmöglich zu Debuggen von Fehlern vermeiden und lots of help von dem .NET erhalten.

+0

Danke! Ich schreibe Code wie diesen, weil ich es plattformübergreifend betreiben muss. Der C-Code ist eine Bibliothek, die sowohl auf Embedded- und Desktop-Linux als auch auf Windows ausgeführt wird. Ich biete eine CLI unter Linux, aber Windows benötigt eine GUI und ich bevorzuge C# für GUIs unter Windows. Ich bin ziemlich neu in der Verwendung von C-Code in C#, daher kenne ich nicht alle Best Practices. Dies war ein trivialisiertes Beispiel meines tatsächlichen Codes, wo ich eine Funktion zur Verfügung stelle, um den in C zugewiesenen Speicher freizugeben. –