Autor Beitrag
xuxu81
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 28.04.18 15:49 
Hallo zusammen,

für mein Projekt wird eine DLL benötigt. Derzeit liegt sie im selben Ordner wie die Anwendung, es soll aber auch die Möglichkeit geboten werden, sie auszulagern (z.B. in einem Unterordner). Mir ist der genaue Pfad also erst zur Laufzeit bekannt, leider erfordert DllImport aber einen konstanten Wert (String).

Weiß zufällig jemand eine elegante Möglichkeit, das ganze dynamisch einzubinden? Oder eine Alternative zu DllImport (bestenfalls mit einem Link zu einem Beispiel was ihr als Praxistest empfehlen könnt)?

Vorab schon mal vielen lieben Dank für eure Zeit und Mühe. :)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4420
Erhaltene Danke: 902


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 28.04.18 17:27 
Entweder du ergänzt den Suchpfad für Dlls via SetDllDirectory so das DllImport auch dieses Verzeichnis durchsucht wenn es versucht die Dll zu laden oder du lädst die entsprechende Dll vorab per LoadLibrary. DllImport benutzt die dann einfach wenn die schon im Speicher ist.

Für diesen Beitrag haben gedankt: xuxu81
xuxu81 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 28.04.18 17:44 
Danke, hilfreicher Tipp! :)

Funktioniert aber nicht, wenn der Name der Bibliothek auch dynamisch ist, oder? Könnte möglicherweise etwas klappen, dass er einfach alle Bibliotheken innerhalb eines Verzeichnis importiert und ich dann direkt auf die Methoden darin zugreifen kann?

Im Grunde sowas wie
ausblenden C#-Quelltext
1:
foreach (string dll in Directory.GetFiles(path, "*.dll")) Assembly.LoadFile(dll);					

was aber nur für die Wrapper Klasse funktionieren würde, nicht für die eigentlichen Bibliotheken (unmanaged).

Moderiert von user profile iconTh69: Code- durch C#-Tags ersetzt
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4420
Erhaltene Danke: 902


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 28.04.18 18:28 
Wenn bei dir alles dynamisch ist bleibt dann nur dynamischer Code und DllImport ist raus. Um noch halbwegs vernünftig zur Designzeit damit arbeiten zu können hilft Marshal.GetDelegateForFunctionPointer

Aber am besten erzählst du erstmal warum du willst was du da willst. Ich erkenne da gerade keinen sinnvollen Anwendungsfall. Ganz zu schwiegen von den Bauchschmerzen die es mir bereitet einfach mal alles was in einem Verzeichnis liegt in den Prozessraum zu laden.
xuxu81 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 28.04.18 19:00 
Also, im Grunde hatte ich für mein Projekt eine DLL und nach einiger Suche auch eine entsprechende Wrapper Klasse gefunden. Die Funktionalität war für meine Wünsche aber noch nicht vollständig, also habe ich beide entsprechend erweitert und möchte diese in naher Zukunft bei Github veröffentlichen.

Allerdings hat es mich schon immer gestört, wenn der Nutzer dann mit den statischen Dateinamen/Pfaden leben, oder entsprechend neu kompilieren muss, anstatt diese einfach nach seinen persönlichen Wünschen in sein Projekt einbinden zu können.

Vereinfacht ausgedrückt habe ich in der Wrapper Klasse derzeit
ausblenden C#-Quelltext
1:
2:
3:
const String _dllimp="blabla.dll";
[DllImport(_dllimp, EntryPoint = "blabla_one", CallingConvention = CallingConvention.Cdecl)]
...

und hätte gerne
ausblenden C#-Quelltext
1:
2:
3:
String _dllimp=GetFileAndPath();
[DllImport(_dllimp, EntryPoint = "blabla_one", CallingConvention = CallingConvention.Cdecl)]
...


Moderiert von user profile iconTh69: Code- durch C#-Tags ersetzt
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3994
Erhaltene Danke: 819

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: So 29.04.18 12:01 
Ich selber habe noch nicht damit gearbeitet, aber du könntest das AppDomain.AssemblyResolve-Ereignis dazu benutzen (d.h. du checkst dadrin auf den vorher fest zugewiesenen DllImport-Namen und lädst dann dynamisch die Assembly).
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2292
Erhaltene Danke: 420

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: So 29.04.18 12:49 
Ich verwende sowas in Delphi. Das hier ist "mein erstes Mal" in C#. :D
Sieht nach viel aus, aber es soll ja ein nachvollziehbares Anwendungsbeispiel sein:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        // Funktion, die dynamisch aus einer (unmanaged) DLL geladen werden soll, hier statisch vorliegend.
        //
        //[DllImport("User32.dll")]
        //private static extern bool GetCursorPos(ref Point lpPoint);


        string _dllFilePath = string.Empty;

        IntPtr _hUser32 = IntPtr.Zero;      
        IntPtr _hGetCursorPos = IntPtr.Zero; 

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate bool _GetCursorPos(ref Point lpPoint);

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            _dllFilePath = @"User32.dll";
            OpenExternLibrary();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            CloseExternLibrary();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Point pt = new Point();
            GetCursorPos(ref pt);
            MessageBox.Show(pt.ToString());
        }

        private bool GetCursorPos(ref Point lpPoint)
        {
            if (_hGetCursorPos != IntPtr.Zero)
            {
                _GetCursorPos gcp = (_GetCursorPos)Marshal.GetDelegateForFunctionPointer(_hGetCursorPos, typeof(_GetCursorPos));
                return gcp (ref lpPoint);
            }
            return false;
        }

        private void OpenExternLibrary()
        {
            _hUser32 = LoadLibrary(_dllFilePath);
            if (_hUser32 != IntPtr.Zero)
                _hGetCursorPos = GetProcAddress(_hUser32, "GetCursorPos");
        }

        private void CloseExternLibrary()
        {
            if (_hUser32 != IntPtr.Zero)
                FreeLibrary(_hUser32);
        }

        // Funktionen, die zum dynamischen Laden einer (unmanaged) DLL benötigt werden
        //
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr LoadLibrary(string lpFileName);
        [DllImport("Kernel32.dll")]
        private static extern IntPtr FreeLibrary(IntPtr hModule);
        [DllImport("Kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
    }
}

Nachtrag

Für die VB-Entwickler:

ausblenden volle Höhe VB.NET-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
Imports System.Runtime.InteropServices

Public Class Form1

    ' Funktion, die dynamisch aus einer (unmanaged) DLL geladen werden soll, hier statisch vorliegend'
    
    '<DllImport("User32.dll")>'
    'Private Shared Function GetCursorPos(ByRef lpPoint As Point) As Boolean'
    'End Function'

    Dim _dllFilePath As String = String.Empty

    Dim _hUser32 As IntPtr = IntPtr.Zero
    Dim _hGetCursorPos As IntPtr = IntPtr.Zero

    <UnmanagedFunctionPointer(CallingConvention.StdCall)>
    Private Delegate Function _GetCursorPos(ByRef lpPoint As Point) As Boolean

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        _dllFilePath = "User32.dll"
        OpenExternLibrary()
    End Sub

    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
        CloseExternLibrary()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim pt As Point = New Point()
        GetCursorPos(pt)
        MessageBox.Show(pt.ToString())
    End Sub

    Private Function GetCursorPos(ByRef lpPoint As Point) As Boolean
        If (_hGetCursorPos <> IntPtr.Zero) Then
            Dim gcp As _GetCursorPos = DirectCast(Marshal.GetDelegateForFunctionPointer(_hGetCursorPos, GetType(_GetCursorPos)), _GetCursorPos)
            Return gcp(lpPoint)
        End If
        Return False
    End Function

    Private Sub OpenExternLibrary()
        _hUser32 = LoadLibrary(_dllFilePath)
        If (_hUser32 <> IntPtr.Zero) Then
            _hGetCursorPos = GetProcAddress(_hUser32, "GetCursorPos")
        End If
    End Sub

    Private Sub CloseExternLibrary()
        If (_hUser32 <> IntPtr.Zero) Then
            FreeLibrary(_hUser32)
        End If
    End Sub

    ' Funktionen, die zum dynamischen Laden einer (unmanaged) DLL benötigt werden
    '

    <DllImport("Kernel32.dll", Charset:=CharSet.Auto)>
    Private Shared Function LoadLibrary(lpFileName As String) As IntPtr
    End Function
    <DllImport("Kernel32.dll")>
    Private Shared Function FreeLibrary(hModule As IntPtr) As IntPtr
    End Function
    <DllImport("Kernel32.dll")>
    Private Shared Function GetProcAddress(hModule As IntPtr, lpProcName As String) As IntPtr
    End Function

End Class

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)


Zuletzt bearbeitet von Frühlingsrolle am Mo 30.04.18 05:05, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: xuxu81
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4420
Erhaltene Danke: 902


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: So 29.04.18 13:00 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Ich selber habe noch nicht damit gearbeitet, aber du könntest das AppDomain.AssemblyResolve-Ereignis dazu benutzen (d.h. du checkst dadrin auf den vorher fest zugewiesenen DllImport-Namen und lädst dann dynamisch die Assembly).

Das funktioniert auch für unmanaged Dlls und nicht nur für Assemblies?

user profile iconxuxu81 hat folgendes geschrieben Zum zitierten Posting springen:
Allerdings hat es mich schon immer gestört, wenn der Nutzer dann mit den statischen Dateinamen/Pfaden leben, oder entsprechend neu kompilieren muss, anstatt diese einfach nach seinen persönlichen Wünschen in sein Projekt einbinden zu können.


Ein weiterer simple Lösung wäre die irgendwo liegende Dll mit irgendeinem Namen einfach durch Code an eine Stelle zu kopieren und passend zu benennen von wo sie dann durch DLLImport geladen wird. Der kopierende Code kann dann irgendeine Konfiguration/Variablen benutzen die mit dem tatsächlichen Namen bzw. tatsächlichen Lagerort umgehen können.

Für diesen Beitrag haben gedankt: xuxu81
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3994
Erhaltene Danke: 819

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: So 29.04.18 14:14 
Ups, da war ich wohl noch nicht ganz wach. ;-) Du hast recht, das geht nur für managed DLLs (Assemblies) - also vergesst den Vorschlag.
xuxu81 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: So 29.04.18 15:18 
Hallo zusammen und vielen Dank für eure Antworten.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Ich verwende sowas in Delphi. Das hier ist "mein erstes Mal" in C#. :D

Danke Dir! :) Hatte den Weg auch schon mehrfach bei meiner Googlesuche gesehen und werde es wohl auch auf die Art machen, wenn ich keine bessere Methode finde. ^^

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Ein weiterer simple Lösung wäre die irgendwo liegende Dll mit irgendeinem Namen einfach durch Code an eine Stelle zu kopieren und passend zu benennen von wo sie dann durch DLLImport geladen wird. Der kopierende Code kann dann irgendeine Konfiguration/Variablen benutzen die mit dem tatsächlichen Namen bzw. tatsächlichen Lagerort umgehen können.

Ich bin nicht sicher, ob ich Dich richtig verstanden habe: Meinst Du die Pfade/Namen im Code statisch zu belassen, dann vor dem Importieren in die eigentliche Anwendung die Bibliotheken zu suchen, an die statisch vorgesehenen Orte zu kopieren und dann mit diesen zu arbeiten und ggf. beim Schließen der App die temporären Kopien wieder zu löschen..?

Könnte ein funktionierendes Workaround sein.