Hallo Community,
ich habe mal eine Frage an euch evtl. kann mir da jemand weiterhelfen.
Hier erstmal der Code..
1
#pragma once
2
3
#include<iostream>
4
#include<Windows.h>
5
6
// The following ifdef block is the standard way of creating macros which make exporting
7
// from a DLL simpler. All files within this DLL are compiled with the DLLAPI_EXPORTS
8
// symbol defined on the command line. This symbol should not be defined on any project
9
// that uses this DLL. This way any other project whose source files include this file see
10
// DLLAPI_API functions as being imported from a DLL, whereas this DLL sees symbols
11
// defined with this macro as being exported.
12
13
#ifdef TUTORIALLIBRARY_EXPORTS
14
#define DLLAPI_API __declspec(dllexport)
15
#else
16
#define DLLAPI_API __declspec(dllimport)
17
#endif
18
19
extern"C"
20
{
21
22
voidDLLAPI_APIInit(void);
23
24
classDLLAPI_APIUser
25
{
26
public:
27
voidtest(void);
28
};
29
}
Ich versuche aktuell eine .dll für ein Excel Projekt zu erstellen.
Die .dll kann ich auch schon erfolgreich in Excel einbinden und
aufrufen.
Die Funktion "init" funktioniert wie sie soll.
Möchte ich jetzt die Funktion "test" aus der Klasse "User" aufrufen,
sagt mir Excel das er diesen Einstiegspunkt in der .dll nicht finden
kann.
Der Aufruf bei Excel ist bei mir wie folgt..
Jan H. schrieb:> Anscheind muss man Klassen anders aufrufen bzw. instanzieren?!
Das muß man, und das hat auch nichts mit Excel, VBA, oder Dlls zu tun.
Da ist zunächst ma C++- Grundwissen gefragt.
Aufrufen kann man nur Funktionen, keine Klassen.
Oliver
Ganz schlechte Idee, C++ für Sprachübergreifende Interfaces zu
verwenden!
C++ kann Funktionsnamen nicht vernünftig schreiben, da nützt dir das
extern "C" nichts. Das ist auch noch Compilerabhängig.
In deinem Fall musst du ?test@User@@QEAAHXZ() aufrufen, nicht test().
Achtung: bei C++ Klassenfunktionen ist der erste Parameter ein
versteckter Zeiger auf die Klassenvariable.
[Warnung voraus: meine Windows-Erfahrungen sind minimal und könnten
veraltet sein.]
Soviel ich weiß, können DLLs nur stdcall-C-Funktionen exportieren.
C++-Klassen brauchen passende C-Wrapper.
Dein "test" müsste also eine globale Funktion sein, die dann ggf eine
Memberfunktion aufruft. Btw, DLLAPI_API auf einer Klasse scheint mir
merkwürdig.
udok schrieb:> Ganz schlechte Idee, C++ für Sprachübergreifende Interfaces zu> verwenden!udok schrieb:> Ganz schlechte Idee, C++ für Sprachübergreifende Interfaces zu> verwenden!>> C++ kann Funktionsnamen nicht vernünftig schreiben, da nützt dir das> extern "C" nichts. Das ist auch noch Compilerabhängig.>> In deinem Fall musst du ?test@User@@QEAAHXZ() aufrufen, nicht test().>> Achtung: bei C++ Klassenfunktionen ist der erste Parameter ein> versteckter Zeiger auf die Klassenvariable.
Okay.
Das scheint auch nur bei Klassen so zu sein. Bei Funktionen außerhalb
einer Klasse wird da nichts hinzu gedichtet sondern die Funktionen
heißen in der .dll genau wie im Quellcode auch.
Hätte ja sein können das es da einen Trick gibt.
foobar schrieb:> Soviel ich weiß, können DLLs nur stdcall-C-Funktionen exportieren.
Nein, DLLs können natürlich auch C++-Methoden exportieren, das ist kein
Problem.
Das Problem entsteht erst bei der Nutzung der DLL durch was anderes als
C++ mit demselben Compiler in derselben Version wie in der DLL
verwendet. Das ist nämlich das einzige, was so eine DLL problemlos
benutzen kann.
Um so'n OO-Gedöhns wenigstens einigermaßen fremd-benutzbar zu machen,
benutzt man in der Windows-Welt typisch das component object model (COM)
als Schnittstelle. Damit können dann auch Programme in vielen anderen
Sprachen umgehen.
Aber C++-Entwickler wären nicht C++-Entwickler, wenn nicht mindestens
jeder zweite auf die dumme Idee kommen würde, so etwas wie COM für seine
"tolle" DLL nochmal neu zu erfinden...
Jan H:
>Die Funktion "init" funktioniert wie sie soll.> Möchte ich jetzt die Funktion "test" aus der Klasse "User" aufrufen,> sagt mir Excel das er diesen Einstiegspunkt in der .dll nicht finden> kann.
Du hast ja auch kein Objekt zur Instanziierung, von dem du die
Memberfunktion aufrufen könntest. Entweder machste die Funktion 'test'
static (sollte gehen) oder bastelst dir halt ne statische Objektfactory,
die dir pointer auf dein C++-Objekt zurückliefert. Das Handle kannste
dann als Argument wieder reingeben - halt WINAPI-mäßig wie bei
CreateEvent/CloseHandle/SetEvent.
c-hater schrieb:> foobar schrieb:>>> Soviel ich weiß, können DLLs nur stdcall-C-Funktionen exportieren.
Nee, das steht glaube ich auch in dem gemangleten Namen drinne, was für
ne Aufrufkonvention genutzt wird. Die Info fällt aber halt bei extern
"C"-Exporten weg.
> Nein, DLLs können natürlich auch C++-Methoden exportieren, das ist kein> Problem.
Man kann ja extern "C" machen für die zu exportierenden Funktionen, halt
aber keine STL-Typen als Parameter/Rückgabetypen nutzen.
> Das Problem entsteht erst bei der Nutzung der DLL durch was anderes als> C++ mit demselben Compiler in derselben Version wie in der DLL> verwendet. Das ist nämlich das einzige, was so eine DLL problemlos> benutzen kann.
Nö, man kann auch mit GCC übersetzte Dlls mit MSVS-C++ laden, kriegt
halt die GCC-Wrapper-Dll zur Laufzeit mit dazugebunden.
> Um so'n OO-Gedöhns wenigstens einigermaßen fremd-benutzbar zu machen,> benutzt man in der Windows-Welt typisch das component object model (COM)> als Schnittstelle. Damit können dann auch Programme in vielen anderen> Sprachen umgehen.
Ist aber seit .net eigentlich bei Windows eher unerwünscht, P/Invoke
bzw. C++/CLI lässt grüßen.
> Aber C++-Entwickler wären nicht C++-Entwickler, wenn nicht mindestens> jeder zweite auf die dumme Idee kommen würde, so etwas wie COM für seine> "tolle" DLL nochmal neu zu erfinden...
COM bedeutet auch massive Schnittstellenkopplung, d.h. man muss sich
ständig mit der Versionierung und Registrierung von COM-Interfaces in
der Registry herumärgern... das ist auch nicht mehr wirklich zeitgemäß,
wenngleich es noch häufig eingesetzt wird, gerade für IPC-Aspekte.
db8fs schrieb:> Nö, man kann auch mit GCC übersetzte Dlls mit MSVS-C++ laden, kriegt> halt die GCC-Wrapper-Dll zur Laufzeit mit dazugebunden.
Dazu muss sie da sein und in einer passenden Version, sonst BUMM...
> Ist aber seit .net eigentlich bei Windows eher unerwünscht, P/Invoke> bzw. C++/CLI lässt grüßen.
Naja...
P/Invoke ist einfach nur ein anderer Name für die klassischen C-Wrapper.
Und C++/CLI IST COM.
> COM bedeutet auch massive Schnittstellenkopplung, d.h. man muss sich> ständig mit der Versionierung und Registrierung von COM-Interfaces in> der Registry herumärgern
Nein. Das gilt nur für bestimmte Sonderformen von COM. Eben die, die
auch über die C++-Welt hinaus benutzt werden können sollen.
Also z.B. von Excel aus. Womit wir dann endlich wieder beim eigentlichen
Thema des Threads wären. Um was von Excel aus benutzen zu können, muss
es entweder ein WINAPI-C-Wrapper haben oder halt so ein spezielles
COM-Objekt sein, für das die Bindungsinformationen in der Registry
abgelegt sein müssen.
c-hater schrieb:> db8fs schrieb:>>> Nö, man kann auch mit GCC übersetzte Dlls mit MSVS-C++ laden, kriegt>> halt die GCC-Wrapper-Dll zur Laufzeit mit dazugebunden.>> Dazu muss sie da sein und in einer passenden Version, sonst BUMM...
Klar, aber die DLL-Hölle hat man unter Win so oder so.
> P/Invoke ist einfach nur ein anderer Name für die klassischen C-Wrapper.> Und C++/CLI IST COM.
Ja, MixedAssemblies müssen auch registriert werden, aber halt ohne sich
mit IUnknown und IDLs rumärgern zu müssen.
>> COM bedeutet auch massive Schnittstellenkopplung, d.h. man muss sich>> ständig mit der Versionierung und Registrierung von COM-Interfaces in>> der Registry herumärgern>> Nein. Das gilt nur für bestimmte Sonderformen von COM. Eben die, die> auch über die C++-Welt hinaus benutzt werden können sollen.
...was aus meiner Sicht erst den Nutzen von COM beschreibt (-> IPC).
Weil wozu für normale Objekte innerhalb eines Projekts sich das antun
und IDLs wie'n Weltmeister zusammenbasteln, wenn's ne normale Klasse
auch tut. Normale rein virtuelle Interface-Klassen hat ja mit COM und
GUIDs und Gedönse nix zu tun.
> Also z.B. von Excel aus. Womit wir dann endlich wieder beim eigentlichen> Thema des Threads wären. Um was von Excel aus benutzen zu können, muss> es entweder ein WINAPI-C-Wrapper haben oder halt so ein spezielles> COM-Objekt sein, für das die Bindungsinformationen in der Registry> abgelegt sein müssen.
Deswegen der Vorschlag C++/CLI. Kannste managed-Wrapper-Objekte für
nativen Code mit machen und brauchst höchstens ein regasm hinterher.
foobar schrieb:> Soviel ich weiß, können DLLs nur stdcall-C-Funktionen exportieren.> C++-Klassen brauchen passende C-Wrapper.
Das stimmt so nicht. Die Aufrufkonvention bestimmt wie die Parameter an
die Funktion in der DLL übergeben werden.
Neben stdcall gibt es auch cdecl. In Pascal gibt es auch noch pascal,
was man allerdings nicht explizit angeben muß, allerdings wird diese
Konvention i.d.R. nur von Programmen verstanden, die selbst in Pascal
geschrieben sind.
In aller Regel tut man gut daran als Methode für den Export stdcall
festzulegen, weil das von vielen Programmiersprachen unterstützt wird.
Eine Klasse zu exportieren ist wohl nicht wirklich der Bringer. Ich habe
das, allerdings bei Objectpascal, immmer über eine Art Wrapperfunktionen
gemacht. Diese haben auch geprüft das die Klasse(n) auch initialisiert
sind. Wenn erforderlich wurde(n) die Klasse(n) initialisiert.
Jan H. schrieb:> Das scheint auch nur bei Klassen so zu sein. Bei Funktionen außerhalb> einer Klasse wird da nichts hinzu gedichtet sondern die Funktionen> heißen in der .dll genau wie im Quellcode auch.> Hätte ja sein können das es da einen Trick gibt.
Der Trick ist, dass du eine virtuelle C++ Klasse verwendest.
In dem Fall ist die Schreibweise egal, du rufst ja alle Funktionen
indirekt über Zeiger auf.
Diese virtuelle Klasse kannst du dann einfach in VB (oder jeder anderen
Sprache) nachbilden. Im wesentlichen hast du ja nur ein Feld von
Funktions-Zeigern,
und du kannst die Namen nach Wunsch in VB neu vergeben.
Diese Klasse hat oft nur virtuelle Funktionen und keine Daten (ausser
einem versteckten Zeiger auf die Tabelle mit den virtuellen Funktionen).
In dem Fall ist es eine reine Interface-Klasse. Das Interace ist also
von der Implementierung durch eine Schicht von virtuellen Funktionen
getrennt.
Du definierst dann noch die globalen Funktionen extern "C" New_Object()
und extern "C" Free_Object(), die dir ein neues Object der Klasse
erzeugen,
bzw. es wieder freigeben. Diese haben "C" Aufrufkonventionen, die
stabil sind, und sich nicht wie der C++ Rotz alle paar Jahre und
Compileriterationen ändern.
Die Funktionen, die die eigentliche Arbeit machen, sind virtuelle C++
Klassen-Funktionen, die nur indirekt über Zeiger angesprochen werden.
Das ist im wesentlichen die Idee von COM (Common Object Model).
Gruss,
Udo