char** -> C# convertieren



  • Ich hab eine Funktion die in C geschrieben wurde nach C# importiert. Was an sich funktioniert. Allerdings liefert mir die Funktion ein char** zurück, was ich mit einem byte[][] kompensiert habe. Das Ergebnis ist allerdings ein Array von Strings...in C# alsoeigentlich string[].

    Die Frage ist nun wie bekomme ich ein string[] aus dem Ergebnis meiner Funktion. Die Lösung mit byte[][] liefert mir immer ein Marshal Exception.

    System.Runtime.InteropServices.MarshalDirectiveException: return value kann nich
    t gemarshallt werden: Ungültige verwaltete/nicht verwaltete Typenkombination (Ar
    rays können nur als Array oder SafeArray gemarshallt werden).
    


  • Es wäre gut, den Code, welcher die Exception wirft, sehen zu können. Als Alternative zu marshaling könntest du unsafe Code in C# verwenden:

    unsafe public static uint strlen(byte *ptr)
    {
    	uint index = 0;
    	while (*(ptr + index++) != 0)
    		;
    	return index;
    }
    
    unsafe public static void *memcpy(void *dest,
                                      void *src,
                                      uint count)
    {
    	while (count-- != 0)
    		*((byte *)dest + count) = *((byte *)src + count);
    	return dest;
    }
    

    Obwohl ich C# sonst eine unangenehme Sprache finde, kannst du schon fast cstdlib nachbilden.



  • Stimmt...währe einfacher mit Code...auch wenns nicht viel ist 🙂

    // Der Import der Funktion
    [DllImport("libmysql.dll",EntryPoint="mysql_fetch_row",CharSet=CharSet.Ansi)]
    private static extern string[] mysql_fetch_row(IntPtr result);
    
    // aufruf in einem Property
    public string[] fetchRow
    {
      get
      {
        if (this.result_ == IntPtr.Zero)
          return null;
    
        // Bei der Zuweisung wird die Exception produziert.
        string[] tmp = mysql_fetch_row(this.result_);
        return tmp;
      }
    }
    

    Reiner experimental code. Ja und ich weis das es eine fertige Schnittstelle für MySQL gibt. Kompiliert wird das ganze ohne murren.

    Ich meine das man das Marshaling beim Import festlegen kann. Bin mir jedoch nicht sicher und in der Doku hab ich bis jetzt noch nichts gefunden.



  • So wie ich das aus den Angaben in der MSDN verstanden habe, lassen sich mehrdimensionale Arrays nicht ohne weiteres marshalen, da die Grösse der einzelnen Dimensionen bekannt sein muss. Wenn es irgendwie möglich ist (was ich nicht glaube), dann geht es mit dem Attribut System::Runtime::InteropServices::MarshalAs. Vielleicht musst du den string[]-Rückgabewert explizit marshalen, damit es Funktioniert. Hier gibt es Informationen zum expliziten marshaling... ka ob es dir etwas nützt.



  • Also es geht auf jedenfall...allerdings ist meine Lösung nicht grad die sauberste und es gibt au bestimmt eine bessere.

    Hier mal die Lösung:

    unsafe private string[] fetch_row()
    {
      byte** result = mysql_fetch_row(this.result_);
      ulong cols = mysql_num_fields(this.result_);
      if (result == null)
        return null;
    
      System.Collections.ArrayList ret = new System.Collections.ArrayList();
      for (ulong c = 0; c < cols; ++c)
      {
        string tmp = "";
    
        byte* b = result[c];
        if (b == null)
        {
          ret.Add(tmp);
          continue;
        }
    
        uint len = CLib.strlen(result[c]);
    
        for (int s = 0; s < len; ++s)
          tmp += Convert.ToChar(b[s]);
        ret.Add(tmp);
      }
    
      string t = "";
      return (string[])ret.ToArray(t.GetType());
    }
    

    Benutzt hab ich deine beiden obigen Methoden...wobei ich strlen korrigieren muste. Das ergebnis wird intern dekrementiert. Sonst bekomm ich das NULL-Byte mit 🙂
    Besonders die vorletzte Zeile geht mir einfach nur auf die Nerven. Gibt es noch eine andere Möglichkeit den Typ eines Datentyps zu bekommen, als über GetType() ?

    Werd mir mal deine Seiten anschauen, maybe sehe ich da etwas passendes.



  • OK. Das ist ja gut das es läuft 👍 . Aus den letzten beiden Zeilen kannst du noch

    return (string[])ret.ToArray(typeof(string));
    

    machen, damit es wenigstens ein bischen angenehmer aussieht.



  • Konnte es jetzt nicht ausprobieren, aber
    vielleicht funktioniert auch folgendes:

    [DllImport("libmysql.dll" ,CharSet=CharSet.Ansi)]
    [return: MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)]
    private static extern string[] mysql_fetch_row(IntPtr result);
    
    public string[] FetchRow() {
        if(result == IntPtr.Zero) {
            return null;
        }
    
        return mysql_fetch_row(result);
    }
    

    MSDN: Standardmäßiges Marshalling für Arrays

    grüße


Anmelden zum Antworten