Hier gibt's auch noch was zu diesem Thema:
https://www.autoitscript.com/forum/topic/18…autoit-possible
https://www.autoitscript.com/forum/topic/18…-clr-framework/
Hier gibt's auch noch was zu diesem Thema:
https://www.autoitscript.com/forum/topic/18…autoit-possible
https://www.autoitscript.com/forum/topic/18…-clr-framework/
Das mit dem Target-Framework war mir schon klar. Allerdings war ich da wegen Kompatiblität skeptisch.
Das mit dem Target-Framework war mir schon klar. Allerdings war ich da wegen Kompatiblität skeptisch.
Das einzige woran das hängen könnte wäre am Target-Framework und an der Tatsache ob netFX überhaupt vorhanden ist.
Da aber mittlerweile wirklich jeder Rechner netFX installiert hat bezweifle ich, dass es da Kompatibilitätsprobleme gibt.
Hallo, alpines, vielen Dank noch mal für dein Tutorial, find ich genial und es funktioniert.
Ein Problem ist aber, dass man mit DLLExport nur statische Methoden exportieren kann und wenn man in dieser statischen Methode eine andere Methode (mit Instanzobjekt als Parameter) aufruft, muss man dieser immer den Objektverweis mitgeben. Wie macht man das? Man kann ja einem DLLCall schlecht eine Instanz mitgeben.
...
public Form1 form;
public Class1()
{
form = new Form1();
}
[DllExport("start", CallingConvention = CallingConvention.StdCall)]
static void start(Form1 form)
{
Application.Run(form);
}
...
Alles anzeigen
...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
Alles anzeigen
Beide Klassen befinden sich im selben Namespace, aber in 2 unteschiedlichen Dateien (wie gewöhnlich). Das Projekt ist allerdings eine Klassenbibliothek.
Vielleicht steh ich im Moment auch voll aufm Schlauch und seh die Lösung wieder net, aber wenn ihr nen Ansatz hättet, währe es nett ihn hier zu posten.
Lg NO1
Da ich leider nicht sehr viel Zeit habe mir das Problem anzuschauen (werde es mir die Tage näher angucken) kann ich dir auf die schnelle nur einen Workaround anbieten.
Arbeite bei deinen Parametern mit den grundlegenden Typen wie Integer, String, Byte, Double, Float, usw.
Für den Parameter kannst du dir dann einfach eine Tabelle anlegen und der form1 den Wert 1 zuweisen.
Dann rufst du die Funktion mit der 1 auf und es wird aus der Objekttabelle, Array, Liste oder wie auch immer du das realisieren möchtest, das passende Objekt genommen.
Es sollte aber möglich sein Pointer auf Datenstrukturen zu übergeben (DLL-Pointerfunktionen), damit solltest du eigentlich einen Pointer auf das Formobjekt übergeben können.
Dann rufst du deine Methode mit dem Pointer zur Funktion auf und kannst sie innerhalb der Methode in ein Klassenobjekt übergeben indem du den Pointer dereferenzierst (sofern das möglich ist).
Danke, dass du dir dafür Zeit genommen hast.
Ich kann auch eine Methode ohne Parameter aufrufen, die dann eine weitere Methode mit der Instanz als Parameter callt. Dann hätten wir das Problem mit DLLCall und den Parametern gelöst, jedoch kann man schon vorher erstellte Objektinstanzen nur in einer nicht statischen Methode mitgeben oder in einer statischen, die dann aber wieder als Parameter die Instanz braucht.
Und das ist dann das eigentliche Problem. Aber danke, dass du dir das Thema näher anschaust.
Lg NO1
So. Da ich grad in der Shoutbox nach dem Thread gefragt hab, wollte ich natürlich auch mit mit dem DLL Export arbeiten.
Problem: Der Compiler spuckt diese Fehlermeldung aus:
Erst: Fehler beim Erstellen des Builds... und dann
Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand
Fehler Unerwarteter Fehler bei der DllExportAppDomainIsolatedTask-Aufgabe.
System.ArgumentException: Der angeforderte Wert "Version46" konnte nicht gefunden werden.
Server stack trace:
bei System.Enum.EnumResult.SetFailure(ParseFailureKind failure, String failureMessageID, Object failureMessageFormatArgument)
bei System.Enum.TryParseEnum(Type enumType, String value, Boolean ignoreCase, EnumResult& parseResult)
bei System.Enum.Parse(Type enumType, String value, Boolean ignoreCase)
bei RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.<>c__DisplayClass8.<GetGetToolPathInternal>b__7(Version version, String toolName) in c:\Users\rober_000\Documents\Code\unmanaged-exports\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:Zeile 568.
bei RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.TryToGetToolDirForFxVersion(String toolFileName, Func`3 getToolPath, String& toolDirectory) in c:\Users\rober_000\Documents\Code\unmanaged-exports\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:Zeile 725.
bei RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.ValidateToolPath(String toolFileName, String currentValue, Func`3 getToolPath, String& foundPath) in c:\Users\rober_000\Documents\Code\unmanaged-exports\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:Zeile 698.
bei RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.ValidateFrameworkPath() in c:\Users\rober_000\Documents\Code\unmanaged-exports\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:Zeile 680.
bei RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.ValidateInputValues() in c:\Users\rober_000\Documents\Code\unmanaged-exports\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:Zeile 396.
bei RGiesecke.DllExport.MSBuild.ExportTaskImplementation`1.Execute() in c:\Users\rober_000\Documents\Code\unmanaged-exports\RGiesecke.DllExport.MSBuild\ExportTaskImplementation.cs:Zeile 264.
bei RGiesecke.DllExport.MSBuild.DllExportAppDomainIsolatedTask.Execute() in c:\Users\rober_000\Documents\Code\unmanaged-exports\RGiesecke.DllExport.MSBuild\DllExportAppDomainIsolatedTask.cs:Zeile 241.
bei System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs)
bei System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg)
Exception rethrown at [0]:
bei System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
bei System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
bei Microsoft.Build.Framework.ITask.Execute()
bei Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
bei Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext() Chromium4AutoIt
Ich habe die aktuelle Version vom UExport, auf x86 hab ich auch gestellt...
Wollte mal wieder die normale Addition testen...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;
namespace Test
{
public class Class1
{
[DllExport("add", CallingConvention = CallingConvention.StdCall)]
static int add(int eins, int zwei)
{
return eins + zwei;
}
}
}
Alles anzeigen
Natürlich gehts auch mit F5 / F7 aber die Funktion wird nicht mal exportiert! (sagt auch der DLLExportViewer)
Wisst ihr, an was das liegt?
System.ArgumentException: Der angeforderte Wert "Version46" konnte nicht gefunden werden.
Version46 scheint glaube ich dein Target-Framework von netFX zu sein, probier mal 3.5, 4.0 oder 4.5 aus, damit sollte es klappen.
Super alpines! Mit 4.5.2 funktioniert's wunderbar. Es gibt nämlich nur 4.6.1 und 4.6 ist die letzte funktionierende Version.
Du solltest vielleicht in Betracht ziehen noch weiter runterzugehen, damit deine DLL mit PCs funktioniert die noch nicht das aktuellste netFX haben.
Meistens benötigt man auch die aktualisierten Funktionen nicht, ich habe für mein aktuelles Projekt z.B. mit der 3.5 exportiert.
Meinst du die .NET Version oder? Aber ja, kann ich machen.
Mal ne andere Frage: Ich muss ein mit der DLL erstelltes Objekt! speichern. Normal würde man ja jetzt die Variablen noch vor dem Konstruktor erstellen, damit alle Methoden der Klasse auf diese zugreifen können. Das geht ja nicht, da es sich um DLL-Aufrufe handelt und die Methoden statisch sind. Ein anderer Weg wäre vielleicht, das erstellte Objekt an den AutoIt DLLCall zurückzugeben und dieses dann später per DLLCall wieder an die DLL zu übergeben. Ich weiß jetzt aber nicht wie ich das machen soll, da die return types bei DLLCall ja nur die standardmäßigen Typen beinhalten und keine Objekte von benutzerdefinierten C# Klassen (was ja auch Schwachsinn wäre). Kann man das irgendwie über die Handles regeln?
Du kannst das Objekt auch static setzen, das ist absolut kein Problem.
Erzeug einfach z.B. ein static string path = null; in der Klassedefinition und greife von deinen Methoden darauf zu.
Sofern du DllOpen verwendest, bleiben die Zuordnungen gültig. Das bedeutet bspw., wenn du in der setPath Methode path = "bla" setzt,
dann wird in deiner getPath Methode auch "bla" zurückgegeben werden.
Du kannst aber auch Structs übergeben und füllen, die erzeugst du in AutoIt und übergibst dann den Pointer.
Mal so gefragt: Was genau willst du denn speichern?
Erzeug einfach z.B. ein static string path = null; in der Klassedefinition und greife von deinen Methoden darauf zu.
Sofern du DllOpen verwendest, bleiben die Zuordnungen gültig. Das bedeutet bspw., wenn du in der setPath Methode path = "bla" setzt,dann wird in deiner getPath Methode auch "bla" zurückgegeben werden.
Danke, das hat mir sehr weitergeholfen. Werde morgen mal schaun, aber der Part funktioniert!
So, vielen Dank nochmal, funktioniert bislang alles wie gewollt.
Wenn es euch nicht stört, würde ich noch mit der Zeit hier Fragen stellen, damit ich mich weiterbilden kann .
Zum Beispiel: Wie benutze ich Arrays mit DLL-Calls? Geht das?
Arrays als solche werden von AutoIt nicht unterstützt, aber du kannst stattdessen einen Pointer zurückgeben.
Returne in deiner Methode ein "int*" und dann erzeugst du aus dem Pointer ein DllStruct mit einem int[]-Eintrag.
Das ist in VS C# leichter gesagt als getan, da Pointer-Arithmetik (im C-Stil) dort als unsafe gehandelt wird.
Du musst diese Option erst im Compiler erlauben (Projekteigenschaften > Build > "Unsicheren Code zulassen") und dann deine Methode als unsafe deklarieren.
Und als ob das nicht reicht musst du auch noch den fixed Operator verwenden um Arrays als Pointer zu verarbeiten in C#.
Das ist alles so kompliziert, da C# eigentlich nicht dafür ausgelegt ist mit Pointern zu arbeiten, sondern diese mehr oder weniger mit Objekten dargestellt sind.
In C/C++ kann man keine Arrays von Funktionen zurückgeben, dort wird das ganze ebenfalls mit Pointern (1D) oder Pointer auf Pointer (2D) gelöst.
Da das ganze wirklich ein bisschen schwierig zu erklären ist hier mal ein kleines Beispiel.
/unsafe muss in den Projekteinstellungen gesetzt sein.
[DllExport("doSomething", CallingConvention = CallingConvention.StdCall)]
public static unsafe int* doSomething(int* count)
{
fixed(int* arrayToReturn = new int[2])
{
arrayToReturn[0] = 1337;
arrayToReturn[1] = 42;
*count = 2;
return arrayToReturn;
}
}
Alles anzeigen
Und so kriegst du das ganze in AutoIt auch wieder raus:
$tCount = DllStructCreate("int")
$pCount = DllStructGetPtr($tCount)
$aRet = DllCall("intArrayReturn.dll", "ptr", "doSomething", "ptr", $pCount)
ConsoleWrite("Count = " & DllStructGetData($tCount, 1) & @CRLF)
$tReturn = DllStructCreate("int[" & DllStructGetData($tCount, 1) & "]", $aRet[0])
ConsoleWrite(DllStructGetData($tReturn, 1, 1) & @CRLF)
ConsoleWrite(DllStructGetData($tReturn, 1, 2) & @CRLF)
$tCount ist dazu da um die Größe des Arrays zu erfassen und der Return des DllCalls beinhaltet den Pointer zu dem Int-Array.
Dann erzeugt man ein Int-Array-Struct mit der passenden Größe mittels $tCount und dem Pointer $aRet[0] und zack du hast dein Array im Struct.
@Churanos die Antwort kommt vielleicht ein wenig spät aber du kannst in der Dll Klassen intern static deklarieren und sie so an deine Markdown-Funktionen weitergeben.
Dann schreibst du noch ein paar Wrapper-Funktionen um diese statischen Instanzen zu füllen und fertig.
Behalt einfach alles was mit markdowninternen Klassen zu tun hat in der Dll und empfange und sende an AutoIt nur die Ergebnisse bzw. Eingaben.
Wow krass... danke für deine Bemühungen hier immer erstmal. Funktioniert einwandfrei, obwohl ich den Code noch nicht ganz verstehe. Danke alpines!
obwohl ich den Code noch nicht ganz verstehe
Ich erklärs dir gerne wenn du mir sagst was du daran nicht verstehst.
Mhh noch ne andere Frage... wenn man Namespaces verwendet, die man über denNuGet-Paketmanager hinzufügt, kommt beim DLL-Aufruf immer die Fehlermeldung ...
"Unbehandelte Ausnahme: System.IO.FileNotFoundException: Die Datei oder Assembly "<Name des Namespaces>, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null" oder eine Abhängigkeit davon wurde nicht gefunden. Das System kann die angegebene Datei nicht finden."
Das liegt daran, dass der Interpreter die Datei im Includeverzeichnis von AutoIt vermutet.
Da kann man glaube ich nichts machen, aber wenn man die Exe compiled und anschließend ausführt, wird die Meldung nicht geschmissen, da im aktuellen WorkingDir danach gesucht wird.
Siehe hier auf dem Bild, ausgeführt in SciTE oder im Explorer als Script: [Blockierte Grafik: https://i.imgur.com/Y3QbvEX.png]
Und ausgeführt als Kompilat: [Blockierte Grafik: https://i.imgur.com/3milMAd.png]