Laden...

DLLImport mit Zeiger als Parameter

Erstellt von thfu vor 16 Jahren Letzter Beitrag vor 16 Jahren 2.810 Views
T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren
DLLImport mit Zeiger als Parameter

Ich bin gerade dabei ein in C++ geschriebenes Terminalprogramm-Tool in C# zu portieren. Dabei soll eine Funktion von einer unverwalteten DLL aufgerufen werden.

Der alte Aufruf (mit C++) sieht so aus:

CLUINT32 numPorts;
clGetNumPorts(&numPorts);

Die Funktion hat den Prototype: INT32 __stdcall clGetNumPorts (UINT32* numPorts)

Jetzt habe ich das in C# folgendermaßen umgesetzt:


[DllImport("clallserial.dll", EntryPoint="clGetNumPorts")]
public extern static unsafe int clGetNumPorts(
            uint* numPorts); // für Anzahl der Ports

mit dem Aufruf:


uint numPorts;

clGetNumPorts(&numPorts);

Leider funktionert es so nicht. Bei dem Aufruf bleibt das Programm hängen.

Meine Frage: Was mache ich falsch? Muß ich noch ein 'MarshalAs' verwenden oder muß ich ein DLLImport Attribut angeben?
Kann ich überhaupt einen Zeigerparameter beim Aufruf verwenden? Oder was muß ich dabei beachten.

2.921 Beiträge seit 2005
vor 16 Jahren

Ich vermute mal, dass "numPorts" ein [out]-Parameter ist, dass heisst der Wird wird in dieser Variablen zurückgegeben, ist das so?

Dann müsste nämlich numPorts nach dem Aufruf die Anzahl der gefunden Ports enthalten. Du hast das ganze aber als normalen In-Parameter definiert.

Ausserdem brauchst du bei dieser Deklaration kein unsafe Schlüsselwort benutzen.

Es sollte auch so gehen.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

Ja numPorts soll wird wieder zurückgegeben werden. Ich werde es gleich am Montag, wenn ich wieder in der Arbeit bin, ausprobieren. Danke für die Hilfe.

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

Ich habe es jetzt wie folgt abgeändert:


[DllImport("clallserial.dll")]
public extern static unsafe int clGetNumPorts(
            out uint* numPorts); // für Anzahl der Ports

mit dem Aufruf:


clGetNumPorts(out &numPorts);

Ich bekomme die Fehlermeldung vom Compiler:
Fehler CS1510: Ein ref- oder out-Argument muss eine zuweisbare Variable sein.

Irgendwas mache ich falsch, ich weis nur nicht was???

Was habe ich falsch verstanden? Hat jemand evtl. eine Art Beispiel?

Was ich auch noch nicht so ganz versehe warum Du [out] in eckigen Klammern geschrieben hast?

215 Beiträge seit 2004
vor 16 Jahren

[DllImport("clallserial.dll")]
public extern static unsafe int clGetNumPorts(
            out uint numPorts); // kein pointer


uint numPorts = 0;
clGetNumPorts(out numPorts);

So sollte es aussehen

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

Danke! Jetzt kann ich es kompilieren. Allerdings bleibt das Programm wieder beim Aufruf von:

clGetNumPorts(out numPorts);

hängen...

...irgendwelche Ideen??

215 Beiträge seit 2004
vor 16 Jahren

nimm statt OUT mal REF

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

...hat auch nichts gebracht! Es bleibt immer noch bei dem Aufruf hängen.

215 Beiträge seit 2004
vor 16 Jahren

2 Tips hab ich noch, dann ist auch bei mir empty:

  1. Nimm das unsafe raus.
  2. Schau mal nach, ob UINT32 auch wirklich ein unsigned integer 32Bit ist. Ich hab da schon die dollsten Sachen erlebt 🙂
T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

zu 1. habe ich probiert, war aber unverändert

zu 2. müßte eigentlich schon UINT32 sein, steht zumindest so in der Doku

Leider kann ich innerhalb der unmanaged DLL anscheinend auch nicht debuggen, sonst würde ich dem schon auf die Spur kommen. Oder gibt es doch eine Möglichkeit zu debuggen?? Fehlermeldung kriege ich auch keine, bleibt einfach hängen.

2.921 Beiträge seit 2005
vor 16 Jahren

oder

[DllImport("clallserial.dll", EntryPoint="clGetNumPorts")]
public extern static int clGetNumPorts(out UInt32 numPorts); // für Anzahl der Ports

Statt auf, kannst Du auch, wie es DaSchroeter schon vorgeschlagen hat auch ref mal ausprobieren.

Heisst der EntryPoint auch wirklich clGetNumPorts?

Was sagt der DependencyWalker?

und noch was ganz wichtiges: Sind die DLLNamen vielleicht dekoriert?
Du brauchst auf jeden Fall genau den Namen den der Dependency-Walker für Dich betreffs der Funktion ausspuckt, sonst wird es nicht funktionieren.

Ansonsten, wenn das nichts hilft, lad mal die DLL hier hoch, wenn es keine Eigenentwicklung sein sollte (sonst darfst Du die ja nicht rausgeben)

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

Hier kommt die DLL gezippt....

Das mit dem dekorieren habe ich nicht ganz kapiert

2.921 Beiträge seit 2005
vor 16 Jahren

thfu: Die DLL ist nicht bzw. die Funktionsnamen sind nicht dekoriert.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;


namespace WindowsApplication3
{
    public partial class Form1 : Form
    {
        [DllImport("clallserial.dll", EntryPoint="clGetNumPorts")]
        public extern static int clGetNumPorts(uint numPorts); // für Anzahl der Ports
        
        public Form1()
        {
            InitializeComponent();
            int n1 = clGetNumPorts(10);
        }
    }
}

Dieser Code liefert bei mir als Ergebnis: -10010, wenn ich zuvor die DLL ins bin/debug Verzeichnis des entsprechenden dazu erstellten Projekt kopiert habe.

Bleibt der bei Dir auch hängen?

In welcher Sprache wurde die clseriadll erstellt?

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

D
500 Beiträge seit 2007
vor 16 Jahren

Hallo!

Leider kann ich innerhalb der unmanaged DLL anscheinend auch nicht debuggen, sonst würde ich dem schon auf die Spur kommen. Oder gibt es doch eine Möglichkeit zu debuggen??

Du kannst eigentlich ganz gut sogar aus dem managed Bereich in den unmanaged Bereich debuggen. Dabei muss man folgendes beachten, vorausgesetzt, dass die unmanaged Dll von Dir stammt:

  • Im Projekt der C# Applikation/Dll findest Du unter "Properties -> Debug" eine Checkbox, die bei mir im Englischen lautet "Enable unmanaged code debugging". Hier setzt Du nun einen Hacken. Von da an, wirst Du nicht mehr die Möglichkeit haben, zur Laufzeit, wenn Du debuggst, den Code abzuändern, was aber nicht so tragisch ist.
  • Die unmanaged Dll mit samt der PDB in das entsprechende Output Verzeichnis kopieren.
  • Nun öffnest Du in Deinem VS, dass das C# Projekt geladen hat, einfach die unmanaged Datei (d.h. die cpp-Datei) und setzt bspw. einen Break-Point in Deiner Unmanaged-Methode.
  • Starte die C# Applikation im Debug-Modus und versuche nun in die importierte Methode zu springen bzw. einfach schauen, ob der Debugger an Deinem in der Cpp-Datei gesetzten Break-Point stehen bleibt.

Gruß, DaMoe

D
500 Beiträge seit 2007
vor 16 Jahren

Tach noch einmal!

Nur eine kurze Frage. Sind denn bei Dir die Funktionen korrekt in der Dll bspw. per


extern "C" 
{
	int _declspec(dllexport) Add(int a, int b);
}

exportiert?

Gruß, DaMoe

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

@DaMoe80:
(zu extern "C"...) Das weis ich nicht. Ich habe leider den Quellcode nicht. Derjenige der es evtl. haben bzw. wissen würde, ist gerade im Urlaub. Vielleicht liegt hier das Problem.
Debuggen kann ich dann wohl ohne DLL-Quellcode auch nicht richtig, aber jetzt weis ich schon mal wie es gehen würde.

@dr4g0n76:
Du hast natürlich auch nicht die entsprechende Hardware (Kamera & Framegrabber) um was auszulesen, aber mein Programm bleibt auch beim Aufruf der externen Methode hängen. Ohne C#, d.h. mit C++, hat es funkioniert.
Ist das mit dem 'nicht dekoriert' ein Vorteil oder ein Nachteil?

2.921 Beiträge seit 2005
vor 16 Jahren

hallo thfu,

Ist das mit dem 'nicht dekoriert' ein Vorteil oder ein Nachteil?

also:

Ich habe es überprüft, die Funktionen sind nicht dekoriert (hab ich mit dem DependencyWalker gesehen).
Dekoration heisst, dass der Compiler best. Zeichen reinmacht, um die Parametergröße in Bytes zu bestimmen und ähnliches. Das heisst der Funktionsname würde dann z.B.

@@??clGetSerial@@

heissen.

Dann würdest Du aber von DllImport eine Exception bekommen ("DLLEntryPoint not found" oder so ähnlich)

Du hast natürlich auch nicht die entsprechende Hardware (Kamera & Framegrabber) um was auszulesen, aber mein Programm bleibt auch beim Aufruf der externen Methode hängen. Ohne C#, d.h. mit C++, hat es funkioniert.

Hast Du das ganze schon mal exakt mit meinem oberen Code-Ausschnitt probiert!?!?

Sonst hat es keinen Sinn hier weiterzudiskussieren.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

Ich habe es jetzt wie folgt abgeändert und jetzt bleibt es nicht mehr hängen.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;


namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        [DllImport("clallserial.dll", EntryPoint = "clGetNumPorts")]
        public extern static int clGetNumPorts(out uint numPorts); // für Anzahl der Ports
        
        public Form1()
        {
            uint numPorts = 10;
            InitializeComponent();
            int n1 = clGetNumPorts(out numPorts);
        }
    }
}

Ich weis blos noch nicht was der Unterschied ist. Irgendwas muß jetzt anders sein als zu meinem Consolenprogramm. Ich weis aber nicht was.

T
thfu Themenstarter:in
31 Beiträge seit 2007
vor 16 Jahren

Ich habe den Unterschied jetzt herausgefunden.

Meine Methode main sieht jetzt so aus:


[STAThread] // Das ist neu
static void Main()

Der Unterschied war das [STAThread]

Danke an alle die mit geholfen haben!!!