Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Unmanaged Dll C++ die Funktionen in C Sharp einbinden
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

Unmanaged Dll C++ die Funktionen in C Sharp einbinden

beantworten | zitieren | melden

Hallo zusammen, Ich habe folgendes Problem:

Ich habe eine C# Application und ein C++ Projekt. MS Visual.net 2015.
Wie rufe ich jetzt von C# eine C++ Funktionen auf.
Suche schon einige Zeit und hab leider noch kein passendes Bsp. gefunden.
Wo könnte man mehr Info findet?
Ich habe eine Toolbox und der Code für sie ist auf C++ geschrieben, wie man oben als Beispiel eine Funktion sehen kann. Dll Datei habe ich zur Verfügung.
Ich wäre sehr dankbar , wenn mir jemand ein bisschen mehr erklären kann.
Vielen Dank!

Hier ist eine Funktion von dem C++ Code:
C ++ Funktion
int Toolbox::Initialize(LPCSTR systemPath, LPCSTR license, DLL_LogFunc* logFunc)
{
   if (m_initialize) return m_initialize((char*)systemPath,(char*)license,logFunc);
   return -1;
}

C Sharp

using System.Runtime.InteropServices;

namespace Project_1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        [DllImport("Toolbox.dll")]
        public static extern int Initialize( ????? ); // Funktion keine Methode. Was muss ich zwischen Klammern schreiben???
        private void btnExecute_Click(object sender, EventArgs e)
        {
              initialize( ???? );
        }
    }
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von doubleII am .
private Nachricht | Beiträge des Benutzers
Yeats
myCSharp.de - Member



Dabei seit:
Beiträge: 97
Herkunft: Österreich

beantworten | zitieren | melden

Hallo,

LPCSTR ist ein string


[DllImport("Toolbox.dll", CharSet = CharSet.Ansi)]
public static extern int Initialize( string systemPath, string license,  IntPtr logFunc);

Zu dem DLL_LogFunc solltest du in der API nachlesen wie hierzu die Definition aussieht. Aber einfach gesagt erwartet sich hier die C++ dll einen Pointer zu einer Methode.
Das kann man mittels eines delegate realisieren.

Sieh hierfür: Marshal Function Pointer

Beispiel für das arbeiten mit einem Delegate:


[UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void GatewayCallback(uint arg0, uint arg1, uint arg2, IntPtr arg3);

[DllImport(@"XXXX.dll",
            CharSet = CharSet.Ansi,
            EntryPoint = "CommSetCallback",
            SetLastError = true,
            CallingConvention = _callingConvention)]
        public static extern void CommSetCallback(GatewayCallback fCall);

Anschließend ist dann noch die Frage des Pointers:
DLL_LogFunc*
der * zeigt an das es sich um einen Pointer handelt.
private Nachricht | Beiträge des Benutzers
weismat
myCSharp.de - Member



Dabei seit:
Beiträge: 878
Herkunft: Frankfurt am Main

beantworten | zitieren | melden

Mittels DllImport kannst Du nur auf C Funktionen und nicht auf C++ Methoden/Objekte zugreifen.
Ob Managed C++ oder ein C Wrapper weniger Arbeit ist, muss Du selber prüfen.
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Vielen Dank für die Info erst mal!

Ich habe heute ein Tipp bekommen. Ich kann die C++ Datei weglassen. D.h. ich darf direkt auf die dll Datei zugreifen (dll Datei ist auf Delphi geschrieben), also ich brauche keine Wrapper Klassen zu erstellen (so weit wie ich gelesen habe). Die Funktionen existieren in der dll Datei ich muss sie ansprechen. Es wurde mir so erklärt, dass ich das C++ Projekt als Muster nutzen kann. Ich habe ins Internet rescher-schiert und habe den Code so geschrieben:

 
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;

namespace Project_1
        private IntPtr logFung;   
     
        [DllImport("Toolbox.dll", CharSet = CharSet.Auto)]
        static extern int Initialize( string systemPath, string license, IntPtr logFung );

        [DllImport("ScorpionVisionToolbox.dll", CharSet = CharSet.Auto )]
        static extern int finalize();

        private void btnExecute_Click(object sender, EventArgs e)
        {
            // Windows DLL (non-.NET assembly)
            //systemPath = Environment.ExpandEnvironmentVariables("%SystemDrive%");
            //if (!systemPath.Trim().EndsWith(@"\"))
            //  systemPath += @"\";
            
            systemPath = @"E:\DLL\Debug\Toolbox.dll";
            

            try
            {
                Assembly assem = Assembly.LoadFile(systemPath);
            }
            catch (DllNotFoundException ex)
            {
                MessageBox.Show(ex.Message);
            }

            //nur als Beispiel 
            license = "F7LRmIFGHcJkqAZfPV4sN1Tuga2SyKJeBL3ZJ0SBZPwRpoeXb8wZUYVcO3EQcwPUfzTQDcOlMC";
            Initialize(systemPath, license, logFung);

 
        }
    }
}

es zeigt mir folgender Fehler:
Ein Ausnahmefehler des Typs "System.IO.FileNotFoundException" ist in mscorlib.dll aufgetreten.

Zusätzliche Informationen: Das System kann die angegebene Datei nicht finden. (Ausnahme von HRESULT: 0x80070002)
Ich habe unter Projekt -> Eigenschaften -> Estellen auf x86 eingestellt, da die Toolbox auf 32 Bit ist, keine Änderung.

Ich verstehe immer noch nicht was ich am IntPtr übergeben muss.

C++:
//Als Muster ich muss sie auf C# umschreiben. Sie existieren in der dll Datei.
int Toolbox::Initialize(LPCSTR systemPath, LPCSTR license, DLL_LogFunc* logFunc)
{
	if (m_initialize) return m_initialize((char*)systemPath,(char*)license,logFunc);
	return -1;
}

int Toolbox::Finalize()

	if (m_finalize) return m_finalize();
	return -1;
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von doubleII am .
private Nachricht | Beiträge des Benutzers
pinki
myCSharp.de - Member

Avatar #avatar-4072.jpg


Dabei seit:
Beiträge: 695
Herkunft: OWL

beantworten | zitieren | melden

Es ist zwar keine Antwort auf die Frage, aber den Lizenzschlüssel solltest du mal fix rauseditieren.
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Er ist nur als Beispiel. Es ist nicht der echte Key. :)
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15690
Herkunft: BW

beantworten | zitieren | melden

Du kannst mit Hilfe von Assembly.Load nur CLR Assemblies bzw. Managed Assemblies wie Managed C++ laden.
Liegt diese Situation vor?

Es spricht vieles dafür, dass entweder die zu ladende DLL fehlt oder, dass eine DLL fehlt, die Deine zu ladende DLL benötigt.
Also evtl. braucht die Toolbox.dll andere DLLs, die Dir fehlen.

Verwende procmon um zu schauen, was genau er versucht zu laden und was fehlt.
Ist ne ziemliche Sisyphusarbeit aber so ists manchmal.

Wenn es sich um eine Managed Assembly handelt, dass kannst sie auch direkt in Visual Studio als Referenz verweisen.
Dann bekommst Du - eventuell - bessere Informationen, was fehlt.
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4001

beantworten | zitieren | melden

Hallo doubleII,

das "Assembly.LoadFile" ist überflüssig, da du ja direkt per P/Invoke ("DllImport") auf die DLL zugreifst (den Zugriff regelt dann schon das .NET-Laufzeitsystem) - und dies würde, wie schon geschrieben, sowie nur für .NET-Assemblies funktionieren.
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Zitat
Du kannst mit Hilfe von Assembly.Load nur CLR Assemblies bzw. Managed Assemblies wie Managed C++ laden.
Liegt diese Situation vor?

Danke! Es klingt logisch. Also wie Th69 geschrieben hat, "Assebly.LoadFile" ist überflüssig.
Zitat
Verwende procmon um zu schauen, was genau er versucht zu laden und was fehlt.
Ist ne ziemliche Sisyphusarbeit aber so ists manchmal.

Ich habe etwas gefunden aber noch nicht ausprobiert. Was meint ihr muss ich was ändern oder passt es so. Wie ich den Code verstehe, er zeigt alle Prozesse.


using System.Diagnostics;
public static void Monitor()
    { 
        ArrayList existingProcesses = GetExistingProcess();  

        while (true)
        {  
            ArrayList currentProcesses = new ArrayList();
            currentProcesses = GetCurrentProcess();

            ArrayList NewApps = new ArrayList(GetCurrentProcess());

            foreach (var p in ExistingProcess)
            {
                NewApps.Remove(p); 
            }
            string str = "";
            foreach (string NewApp in NewApps)
            {
                str = "Process Name : " + NewApp + "   Process ID : " + System.Diagnostics.Process.GetProcessesByName(NewApp)[0].Id.ToString() + " ";
            }
            MessageBox.Show(str);
        }
    }

Hat jemand eine Ahnung was der Pointer "IntPtr logFunc" bekommt? es ist mir nicht ganz klar. :(
Dieser Beitrag wurde 4 mal editiert, zum letzten Mal von doubleII am .
private Nachricht | Beiträge des Benutzers
dannoe
myCSharp.de - Member



Dabei seit:
Beiträge: 157

beantworten | zitieren | melden

Ich glaube mit "procmon" war eigentlich das hier gemeint: Process Monitor

Das Tool zeigt dir alle Dateisystem/Registry Aktionen, die deine Programm macht. So kannst du z.B. herausfinden ob er versucht Dateien aus dem falschen Pfad zu laden o.ä.

Und "logFunc" hört sich für mich nach einer Log-Methode an. Hast du keine Dokumentation für die DLL?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von dannoe am .
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Ich bin ein Schritt weiter gekommen. Ich habe Zugriff auf der Toolbox. Ich habe viel ausprobiert und für die, die gleiches Problem wie ich haben. SEHR WICHTIG!

[DllImport("Pfad\\DLLdatei.dll")]
Beispiel: [DllImport("E:\\Laser\\Debug\\Toolbox.dll")]
Wenn man die DLL Datei ins system32 kopiert funktioniert auch nicht, die Dll Datei wird nicht gefunden.

Könnte mir jemand sagen, ob ich die funktion richtig ins C sharp umgeschrieben habe?
Das ist die Funkt. von Dll Datei:
typedef void LogFunc(char* msg);

int  Initialize(const char* WorkingDirectory,
                const char* License,
                LogFunc*    logFunc);

Parameters:
WorkingDirectory - writeable location which may be used by the library for configuration files, logging and debugging purpose.
License - Tordivel supplied license string to enable toolbox image processing.
logFunc - callback function for info and debug messages. Normally set to NULL.
Returns:
errorcode or 0 for succeed


ich habe den Code jetzt so umgeschrieben:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Runtime.InteropServices;


namespace Project_1
{
   // ????
    public delegate void DLL_Func(string logFunc);
    public partial class Form1 : Form
    {
        
        public Form1()
        {
            InitializeComponent();
        }
        
        [DllImport("E:\\Laser\\Debug\\Toolbox.dll")]
        static extern int Initialize(string systemPath, string license, DLL_Func logFunc);

        private void btnExecute_Click(object sender, EventArgs e)
        {
            systemPath = @"E:\DLL\Debug\";
            license = "zFm6pwdkz";

            try
            {

                Form1.Initialize(systemPath, license, logFunc);
              
            }


            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                //MessageBox.Show(ex.Message);
            }

        }
    }
}



Für die logFunc bin ich mir nicht sicher, ob man sie so als Delegate eingeben kann und der Wert, den sie liefert irgendwie ist mir nicht klar, ob es so richtig ist. Wäre dankbar, wenn mich jemand korrigieren kann. Es zeigt mir folgendes als Fehlermeldung:

"Zusätzliche Informationen: Ein Aufruf an die PInvoke-Funktion "Project_1!Project_1.Form1::Initialize" hat das Gleichgewicht des Stapels gestört. Wahrscheinlich stimmt die verwaltete PInvoke-Signatur nicht mit der nicht verwalteten Zielsignatur überein. Überprüfen Sie, ob die Aufrufkonvention und die Parameter der PInvoke-Signatur mit der nicht verwalteten Zielsignatur übereinstimmen."

Danke!
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von doubleII am .
private Nachricht | Beiträge des Benutzers
Yeats
myCSharp.de - Member



Dabei seit:
Beiträge: 97
Herkunft: Österreich

beantworten | zitieren | melden


public static class NativeMethods
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

        [DllImport("kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetDllDirectory(string lpPathName);
    }
Damit kann man eine Dll in den Speicher laden und verwenden.
Das kann dann so verwendet werden:


SetDllDirectory(@"..\..\..\DLL");
LoadLibrary("Toolbox.dll");


[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void DLL_Func(string logFunc);

Mfg
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Yeats am .
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Das kling super. Kurz zusammengefasst, ob ich es richtig verstanden habe.
1. Die static Klasse erstellen


public static class NativeMethods
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

        [DllImport("kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetDllDirectory(string lpPathName);
    }
jetzt kann ich im Speicher dll Datei laden.

2. Hier gebe ich den Pfad zur dll Datei und sie wird geladen. Der Zusammenhang mit der anderen Klasse Form1 ist mir nicht klar. :(

SetDllDirectory(@"..\..\..\DLL");
LoadLibrary("Toolbox.dll");

######################################

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void DLL_Func(string logFunc);

Gerade habe getestet. Der gleiche Fehler wird rausgespuckt. :(


using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Runtime.InteropServices;


namespace Project_1
{
   //geändert
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void DLL_Func(string logFunc);

    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }
        
        [DllImport("E:\\Laser\\Debug\\Toolbox.dll")]
        static extern int Initialize(string systemPath, string license, DLL_Func logFunc);

        private void btnExecute_Click(object sender, EventArgs e)
        {
            systemPath = @"E:\DLL\Debug\";
            license = "zFm6pwdkz";

            try
            {

                Form1.Initialize(systemPath, license, logFunc);

            }


            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                //MessageBox.Show(ex.Message);
            }

        }
    }
}
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von doubleII am .
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Hallo Yeats,
ich habe das Problem gelöst jetzt funktioniert.
Könntest du mir etwas mehr den Code oben erklären.
Danke Yeats,

Die Lösung:



    //geändert
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void DLL_Func(string logFunc);
   [DllImport("E:\\Laser 2000\\Debug\\Toolbox.dll",  CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 
Dieser Beitrag wurde 4 mal editiert, zum letzten Mal von doubleII am .
private Nachricht | Beiträge des Benutzers
Yeats
myCSharp.de - Member



Dabei seit:
Beiträge: 97
Herkunft: Österreich

beantworten | zitieren | melden

Was möchtest du wissen?
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Hallo Yeats,

Das kling super. Kurz zusammengefasst, ob ich es richtig verstanden habe.
1. Die static Klasse erstellen

public static class NativeMethods
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

        [DllImport("kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetDllDirectory(string lpPathName);
    }
jetzt kann ich im Speicher dll Datei laden.
Wie verbinde ich die Klasse jetzt mit meiner. Mach bitte ein Beispiel.
Vielen Dank!
private Nachricht | Beiträge des Benutzers
Yeats
myCSharp.de - Member



Dabei seit:
Beiträge: 97
Herkunft: Österreich

beantworten | zitieren | melden

Hallo,

Bezieh mich hier auf ein Projekt an dem ich gerade arbeite.

Ich beziehe mich auf XC_Comm.dll. Diese Dll liegt in einem Ordner. Zuerst rufe ich die Methode SetDllDirectory auf und gib den relativen Pfad an in dem die Dll liegt.
Anschließend rufe ich die Methode LoadLibrary auf, bei dieser gebe ich den Namen der dll an.

Momentan ist das ganze noch ein Prototyp und nicht nach MVVM gebaut. Somit liegt mein Code noch im Code-Behind-File der Wpf Anwendung.

MainWindow.xaml.cs:


public void SetAndLoadDll()
{
if (!NativeMethods.SetDllDirectory(@"..\..\..\DLL"))
return;
var handler = NativeMethods.LoadLibrary("XC_Comm.dll");
}

handler sollte beim Schließen benutzt werden um die dll wieder frei zu geben(FreeLibrary).

Grüße
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4001

beantworten | zitieren | melden

Hallo Yeats und doubleII,

ich habe doch schon geschrieben, daß es gar nicht nötig ist, die DLL selbst dynamisch zu laden, da dies alles per P/Invoke gemacht wird!
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Th69 am .
private Nachricht | Beiträge des Benutzers
Yeats
myCSharp.de - Member



Dabei seit:
Beiträge: 97
Herkunft: Österreich

beantworten | zitieren | melden

Hallo Th69,

Dies aber nur solange die DLL im GAC oder im selben Programm Verzeichnis liegt.
private Nachricht | Beiträge des Benutzers
doubleII
myCSharp.de - Member



Dabei seit:
Beiträge: 33

Themenstarter:

beantworten | zitieren | melden

Hallo TH69,


Wie meinst du das? ich habe die Dll Datei unter debug, Release, sogar unter
System32 gespeichert , funktioniert nicht. Oder ich mache etwas falsch? Ich bin offen, wenn man etwas einfach lösen kann.

Schöne Grüße
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15690
Herkunft: BW

beantworten | zitieren | melden

Und wie.
DLLs registriert man in den GAC via gacutil.exe
Der GAC liegt übrigens unter %windir%\Assembly

Hinweis: niemals den GAC ohne gacutil anfassen.

Ansonsten müssen die DLL im gleichen Verzeichnis wie die Exe liegen.
Wenn das nicht funktioniert; hab Dir bereits weiter vorne im Thread gesagt, dass man mit procmon diese Art von Fehler aufspürt um zu schauen, was er versucht zu laden.
private Nachricht | Beiträge des Benutzers