Forum: PC-Programmierung C++ > DLL export > Excel VBA


von Jan H. (janiiix3)


Lesenswert?

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
  void DLLAPI_API Init(void);
23
24
  class DLLAPI_API User
25
  {
26
  public:
27
    void test(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..
1
Declare PtrSafe Function User Lib "F:\Dropbox\foo\TutorialLibrary.dll" ()
2
 Sub foo()
3
 Call User.test
4
End Sub

Anscheind muss man Klassen anders aufrufen bzw. instanzieren?!

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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

von udok (Gast)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

[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.

von Jan H. (janiiix3)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von db8fs (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von db8fs (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von udok (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.