Laden...

Übergabe von C# String-Array zum Füllen durch/an C++ Dll? AccessViloation

Erstellt von ati_sah vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.873 Views
A
ati_sah Themenstarter:in
8 Beiträge seit 2013
vor 9 Jahren
Übergabe von C# String-Array zum Füllen durch/an C++ Dll? AccessViloation

Hallo,

ich habe ein C# HauptProjekt mit C++ DLL UnterProjekt:
Im C# habe ich ein String[] Array Zeilen=6 und Feldbreite=6 Zeichen, die im C++ dll gefüllt wird und an C# zurückgegeben wird. Ich will den gesamten Array mit einem Zeiger übergeben.
Dabei kommt am C++ FunktionAufruf der Fehler > Fehlermeldung:

AccessViolationException und Array bleibt unverändert ?:


[DllImport("myDLL.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void Test(/*[MarshalAs(UnmanagedType.LPArray, SizeConst = 2*6)]*/ string[] s, int count);

private void button4_Click(object sender, EventArgs e)
{
  /* C#: ArrayErgebnisAufbau:
  string[] strings = new[6]:
  strings [0]="00:01",
  strings [1]="01:02",
  strings [2]="02:03",
  strings [3]="03:04",
  strings [4]="04:05",
  strings [5]="05:06";*/

  string[] strings = new[6]; 

  // Füllung/Ausgabe: VorÄnderung
  for (int i = 0; i < 6; ++i)
  {
   strings[i] = String.Format("{0:00}:{1:00}", i, i+1);

   ListViewItem item = new ListViewItem();
   item.Text = i.ToString();

   item.SubItems.Add(strings[i].ToString());
   lv.Items.Add(item);
  }

  // Änderung
  Test(strings, strings.Length);

  // Ausgabe: NachÄnderung
  for (int i = 0; i < strings.Length; ++i)
  {
   ListViewItem item = new ListViewItem();
   item.Text = i.ToString();

   item.SubItems.Add(strings[i].ToString());
   lv.Items.Add(item);
  }
}

C++:

void Test(wchar_t** s, int count)
{
  wchar_t** wct = new wchar_t*[6*count];

  for(int i=0; i < count; i++)
  {
    swprintf(wct[i], 6*sizeof(wchar_t), L"%02d:%02d\0", i+1, i+2);
  }
}

MFG

16.830 Beiträge seit 2008
vor 9 Jahren

Bist Du Dir sicher, dass Du mit StdCall und nicht mit Cdecl arbeitest?

Für die Deklaration fehlt dem Marshal der Arraytyp, der je nach Zugriffsrichtung auch mit In / Out versehen werden sollte, Zb

 [In][Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] 

Dem Code nach wäre es übrigens ein pures Schreiben, kein ändern; Du holst ja vorher nicht ein bestehendes Array, sondern übergibst nur eines.

B
357 Beiträge seit 2010
vor 9 Jahren

Ich hab das vor einiger Zeit mal mit einer kleinen Struktur gelöst, als "Geht das überhaupt?"-Projekt. Vielleicht kannst du daraus was lesen, was dir weiterhilft.


static class Program
    {
        [DllImport("Unmanagedtest.dll", CallingConvention = CallingConvention.Cdecl)]
        unsafe public static extern int Modify(IntPtr taest);

        [DllImport("Unmanagedtest.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Multiply(int i, int j);

        unsafe static void Main(string[] args)
        {
            testgedings tast = new testgedings();
            tast.iArgh = 5;
            tast.iBrgh = 10;
            tast.strBla = "Sers";

            arr[] ah = new arr[2];
            ah[0] = new arr();
            ah[1] = new arr();
            ah[0].haha = 1;
            ah[0].strInner = "Ich bin 1";
            ah[1].haha = 2;
            ah[1].strInner = "Ich bin 2";
            tast.harr = ah;

            int size = Marshal.SizeOf(typeof(testgedings));
            for (int i = 0; i < tast.harr.Length; i++)
            {
                size += Marshal.SizeOf(tast.harr[i]);
            }
            IntPtr unmanagedAddr = Marshal.AllocHGlobal(size);

            Marshal.StructureToPtr(tast, unmanagedAddr, true);

            Modify(unmanagedAddr);

            Marshal.PtrToStructure(unmanagedAddr, tast);

            Marshal.FreeHGlobal(unmanagedAddr);
            unmanagedAddr = IntPtr.Zero;
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class testgedings
    {
        public int iArgh;
        public int iBrgh;
        [MarshalAs(UnmanagedType.BStr, SizeConst = 100)]
        public string strBla;
        [MarshalAs(UnmanagedType.ByValArray)]
        public arr[] harr;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class arr
    {
        public int haha;
        [MarshalAs(UnmanagedType.BStr, SizeConst = 100)]
        public string strInner;
    }

Die C++ Methode sah bei mir so aus:


int __declspec(dllexport) Modify(teststruct * tst){

std::wstring sr (L"Harlow!");
tst->iArgh = 299;
tst->iBrgh = 999;
tst->strBla = SysAllocString(sr.c_str());
arraydings ha;
ha.hirgl = 9999;
std::wstring si(L"Ich bin innen");
ha.strInner = SysAllocString(si.c_str());
tst->arr = ha;	

arraydings aha;
SafeArrayGetElement(tst->sfarr, 0, &aha);
std::wstring si1(L"Ich bin 1 NEU");
aha.strInner = SysAllocString(si1.c_str());
	
return 1;
};

Ich weiß, das ist furchtbar lesbar mit meinen Bezeichnern, aber ist jetzt halt rein aus unserem POC-Projekt rauskopiert.

String-Arrays aus C# haben wir auf C++ Seite immer als SAFEARRAY behandelt. Darin liegen BSTR-Objekte, mit denen man arbeiten kann.

A
ati_sah Themenstarter:in
8 Beiträge seit 2013
vor 9 Jahren

Hallo, vielen dank

  1. Kopierfehler: [DllImport("myDLL.dll", CallingConvention = CallingConvention.Cdecl)]
  2. public static extern void Test([In][Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] s, int count);
  3. das Holen des bestehenden Arrays, ist das nicht mit Übergabe des gesamten strings Arrays erledigt ?

nun mekert er nicht mehr aber er liefert mir nur "0"'s dh nur das 1. Zeichen 1Zeichen zurück, wie kann ich nun alle Strings zurückgeben?:

void Test(wchar_t** s, int count)
{
  for(int i=0; i < count; i++)
  {
    swprintf(s[i], 6*sizeof(wchar_t), L"%02d:%02d\0", i, i+1);
   }
}

MFG

Hinweis von Coffeebean vor 9 Jahren

Beiträge zusammengefasst