Benutzer mit den meisten Antworten
C#-Zugriff auf C-DLL-Funktion mit double-Array-Parameter unter WINDOWS 64bit

Frage
-
Ein freundliches Hallo an die Foren-Mitglieder,
ich bitte Euch um Unterstützung bei der Lösung folgendes Problems:
(zur Abkürzung habe ich die Beschreibung vereinfacht und nur auf das wesentlich beschränkt.)
Eine "reine C-Funktionen-DLL" (mit Microsoft Visual Studio 2005 erstellt) enthält eine C-Funktion,
die nur einen double-Zeiger(!) als Funktionsparameter hat, der zur Aufnahme der Start-Adresse eines (z.B.) 10-elementigen double-Vektors dient.
Die C-Funktion setzt dann einfach nur den (double-)Inhalt der "ersten 10 Addressen" auf 5.
Diese reine C-Funktionen-DLL wird nun in zwei Version als "32bit"-DLL (Fkt32.dll) und als "64bit"-DLL (Fkt64.dll) erstellt.
In einem C#-(Forms)-Programm (auch mit Microsoft Visual Studio 2005/.NET Framework 2.0 erstellt) werden nun in zwei unterschiedlichen statischen Klassen
die Funktion aus je einer der beiden o.a. "reinen C-Funktionen-DLLs" deklariert.
Das C#-(Forms)-Programm ruft dann je nach Wert von "IntPtr.Size" (4 oder 8) die C-Funktion aus der entsprechenden statischen Klasse auf (mit einem vorher auf 10 double-Elementen dimensionierten Variablen als Funktionsparameter).
Ergebnis:
Funktioniert prima auf einem 32bit-PC, funktioniert NICHT(!) auf einem 64bit-WINDOWS 2008 R2-Server:
Nach dem Aufruf ist nur das erste Element mit 5 belegt, alle anderen Elemente sind 0.
Falls man (im aufrufenden Programm und in der C-Funktionen-DLL) für den double-Vektor statt 10 Elemente 30 Elemente dimensioniert und auch verarbeitet,
stürzt das aufrufende Programm sogar "meldungslos" ab.
Wenn die (64bit) C-Funktionen-DLL aus einem (6bit) C-EXE-Programm (auch mit Microsoft Visual Studio 2005 erstellt) aufgerufen wird, funktioniert es richtig.
Folglich liegt es wohl nicht an einer evtl. falschen "Zeiger-Arithmetik" in der C-Funktionen-DLL, oder ?
Mein Fazit: In einem C# (.NET 2.0) werden double-Vektoren (double-Arrays) bei Ausführung auf einem 64bit-PC offensichtlich "(wo)anders im Speicher dimensioniert" als auf einem 32bit-PC.Bei C-Funktionen nur mit null-dimensionalen Parametern (= ohne Zeiger-Parameter, die auf ein Array zeigen) funktioniert der Aufruf aus C# auch unter 64bit prima.
Was mache ich falsch ? Bin für jeden Tipp/Hinweis dankbar.
(Da vielleicht jemand sofort eine Lösung hat, verzichte ich erstmal auf entsprechenden Quellcode. Und: Unter 32bit-Systemen haben wir seit einigen Jahren viele C-Funktionen-DLLs in Visual Basic 6, EXCEL 2003, C-Programmen, .NET-C#-Programmen und .NET-C#-DLLs erfolgreich eingebunden, auch mit double-Vektoren und double-Arrays etc.
Herzlichen Dank im voraus !
Frank Massel
Antworten
-
Ich habe mal weiter experimentiert und dabei folgende (einfache) Lösung gefunden:
(Hinweis: Ich benötige nur ein in C zu belegendes Double-Array fester Größe.)
-
Man deklariert in der reinen C-Funktionen-DLL die entsprechende Funktion wie folgt:
short __declspec(dllexport) __stdcall Test(double DoubleArray[3][4]); short __declspec(dllexport) __stdcall Test(double DoubleArray[3][4]) { short j; for(short i = 0; i < 3; i++) { for(j = 0; j < 4; j++) { DoubleArray[i][j] = i * 10 + j; } } return 0; }
und kompiliert das einmal als Test32.dll (mit WIN32) und einmal als Test64.dll (mit x64).
-
Im C#-.NET-Programm deklariert man die Funktion aus den beiden o.a. C-DLLs in zwei statischen Klassen und ruft diese Funktionen in Abhängigkeit von IntPtr.Size auf:
public static class DLL_Zugriff_32 { [DllImport(@"D:\Irgendwo\Test32.dll", CallingConvention = CallingConvention.Cdecl)] public static extern short Test(double[,] DoubleArray); } public static class DLL_Zugriff_64 { [DllImport(@"D:\Irgendwo\Test64.dll", CallingConvention = CallingConvention.Cdecl)] public static extern short Test(double[,] DoubleArray); } /* Die folgende Funktion muss in einer Klasse (z.B. Form) untergebracht werden. */ public void DLL_FunktionsAufruf() { double[,] DoubleArray = new double[3, 4]; switch (IntPtr.Size) { case 4: Rückgabe = DLL_Zugriff_32.Test(DoubleArray); break; case 8: Rückgabe = DLL_Zugriff_64.Test(DoubleArray); break; default: throw new ArgumentOutOfRangeException("IntPtr.Size", IntPtr.Size, "Der Wert wird in der Fallunterscheidung nicht berücksichtigt."); } }
Hinweis: In C werden Arrays fester Größe als <Datentyp> Array[][] deklariert, in C# als <Datentyp> Array[,] ! In C# deklariert ein <Datentyp> Array[][] ein sog. "Array von Arrays" (kann man, wie ich, schon mal drauf reinfallen...)
Mit dieser Lösung kann man in den C-Funktionen (endlich) auf die Zeiger verzichten.
Das ganze funktioniert auch nur mit eindimensionalen Vektoren, wobei dann sogar auf die Länge bei den Funktions-Deklarationen verzichtet werden kann (aber sicherheitshalber dann die Länge des Vektors in einem zusätzlichen Parameter übergeben werden sollte):
In Kurzform ohne den empfohlenden "Längen-Parameter":
In C:
short __declspec(dllexport) __stdcall Test(double DoubleVektor[]);
...In C#:
[DllImport(@"D:\Irgendwo\Test64.dll", CallingConvention = CallingConvention.Cdecl)] public static extern short Test(double[] DoubleVektor);
Vielen Dank an diejenigen, die sich mit meiner Frage beschäftigt haben !
Viele GrüßeFrank Massel
- Als Antwort markiert Heubeck-EDV Dienstag, 3. April 2012 21:41
Alle Antworten
-
Ich habe mal weiter experimentiert und dabei folgende (einfache) Lösung gefunden:
(Hinweis: Ich benötige nur ein in C zu belegendes Double-Array fester Größe.)
-
Man deklariert in der reinen C-Funktionen-DLL die entsprechende Funktion wie folgt:
short __declspec(dllexport) __stdcall Test(double DoubleArray[3][4]); short __declspec(dllexport) __stdcall Test(double DoubleArray[3][4]) { short j; for(short i = 0; i < 3; i++) { for(j = 0; j < 4; j++) { DoubleArray[i][j] = i * 10 + j; } } return 0; }
und kompiliert das einmal als Test32.dll (mit WIN32) und einmal als Test64.dll (mit x64).
-
Im C#-.NET-Programm deklariert man die Funktion aus den beiden o.a. C-DLLs in zwei statischen Klassen und ruft diese Funktionen in Abhängigkeit von IntPtr.Size auf:
public static class DLL_Zugriff_32 { [DllImport(@"D:\Irgendwo\Test32.dll", CallingConvention = CallingConvention.Cdecl)] public static extern short Test(double[,] DoubleArray); } public static class DLL_Zugriff_64 { [DllImport(@"D:\Irgendwo\Test64.dll", CallingConvention = CallingConvention.Cdecl)] public static extern short Test(double[,] DoubleArray); } /* Die folgende Funktion muss in einer Klasse (z.B. Form) untergebracht werden. */ public void DLL_FunktionsAufruf() { double[,] DoubleArray = new double[3, 4]; switch (IntPtr.Size) { case 4: Rückgabe = DLL_Zugriff_32.Test(DoubleArray); break; case 8: Rückgabe = DLL_Zugriff_64.Test(DoubleArray); break; default: throw new ArgumentOutOfRangeException("IntPtr.Size", IntPtr.Size, "Der Wert wird in der Fallunterscheidung nicht berücksichtigt."); } }
Hinweis: In C werden Arrays fester Größe als <Datentyp> Array[][] deklariert, in C# als <Datentyp> Array[,] ! In C# deklariert ein <Datentyp> Array[][] ein sog. "Array von Arrays" (kann man, wie ich, schon mal drauf reinfallen...)
Mit dieser Lösung kann man in den C-Funktionen (endlich) auf die Zeiger verzichten.
Das ganze funktioniert auch nur mit eindimensionalen Vektoren, wobei dann sogar auf die Länge bei den Funktions-Deklarationen verzichtet werden kann (aber sicherheitshalber dann die Länge des Vektors in einem zusätzlichen Parameter übergeben werden sollte):
In Kurzform ohne den empfohlenden "Längen-Parameter":
In C:
short __declspec(dllexport) __stdcall Test(double DoubleVektor[]);
...In C#:
[DllImport(@"D:\Irgendwo\Test64.dll", CallingConvention = CallingConvention.Cdecl)] public static extern short Test(double[] DoubleVektor);
Vielen Dank an diejenigen, die sich mit meiner Frage beschäftigt haben !
Viele GrüßeFrank Massel
- Als Antwort markiert Heubeck-EDV Dienstag, 3. April 2012 21:41
-
Nachtrag (zur Vollständigkeit):
Einen Wermuthstropfen hat die o.a. Lösung doch:
Die ohne Zeiger deklarierte C-Funktion ist z.B. aus EXCEL-VBA NICHT aufrufbar (wenigstens bei EXCEL 2003 32bit).
Also muss man in der C-Funktionen-DLL zwei C-Funktionen als "DLL-Schnittstelle" einrichten:
a)
Eine C-Funktion mit einem double-Zeiger-Parameter. In dieser Funktion muss dann auch die ganze Programmlogik enthalten sein (leider weiterhin mit Zeiger-Arithmetik...).short __declspec(dllexport) __stdcall Test_VBA(double *ZeigerAufDoubleArray);
short __declspec(dllexport) __stdcall Test_VBA(double *ZeigerAufDoubleArray)
{
short j;for(short i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
*(ZeigerAufDoubleArray + i * 4 + j) = i * 10 + j;
}
}
return 0;
}b)
Eine C-Funktion mit einem double-Array-Parameter (wie in meiner vorherigen Nachricht beschrieben).
In dieser Funktion wird die Funktion aus a) mit der Adresse des ersten Array-Elementes aufgerufen.short __declspec(dllexport) __stdcall Test_C_Sharp(double DoubleArray[3][4]);
short __declspec(dllexport) __stdcall Test_C_Sharp(double DoubleArray[3][4])
{
return Test_VBA(&DoubleArray[0][0]);
}
In EXCEL-VBA kann dann die C-Funktion aus a), in C# dann die C-Funktion aus b) eingebunden werden.
Viele Grüße
Frank Massel
-
In C:
short __declspec(dllexport) __stdcall Test(double DoubleVektor[]);
...In C#:
[DllImport(@"D:\Irgendwo\Test64.dll", CallingConvention = CallingConvention.Cdecl)] public static extern short Test(double[] DoubleVektor);
-
Hallo Ansgar,
auch wenn damit meine Tests alle funktioniert haben, hast Du Recht:
In allen "meinen" C#-Deklarationen von C-DLL-Funktionen (in meinen vorherigen Antworten) bitte
CallingConvention = CallingConvention.Cdecl
ERSETZEN DURCH
CallingConvention = CallingConvention.StdCall
(= die Standardbelegung: Kann also auch weggelassen werden).
Danke für den Hinweis !
Viele Grüße
Frank Massel