Zugreifen auf DLLs in Excel
Gilt für: Excel 2013 | Office 2013 | Visual Studio
Sie können auf eine DLL-Funktion oder einen Befehl in Microsoft Excel auf verschiedene Arten zugreifen:
Über ein Microsoft VBA-Codemodul (Visual Basic for Applications), in dem die Funktion oder der Befehl mithilfe einer Declare-Anweisung verfügbar gemacht wurde
Über eine XLM-Makrovorlage mithilfe der Funktion CALL oder REGISTER
Direkt aus dem Arbeitsblatt oder von einem angepassten Element in der Benutzeroberfläche
XLM-Funktionen werden in dieser Dokumentation nicht behandelt. Verwenden Sie nach M�glichkeit eine der beiden anderen Methoden.
F�r den direkten Zugriff aus dem Arbeitsblatt oder von einem angepassten Element der Benutzeroberfl�che muss die Funktion oder der Befehl zun�chst bei Excel registriert werden. Informationen �ber das Registrieren von Befehlen und Funktionen finden Sie unter Zugriff auf XLL-Code in Excel (engl.).
Aufrufen von DLL-Funktionen und -Befehlen in VBA
In VBA k�nnen Sie mit der Declare-Anweisung auf DLL-Funktionen und -Befehle zugreifen. Diese Anweisung verf�gt �ber eine Syntax f�r Befehle und eine f�r Funktionen.
Syntax 1 � Befehle
[Public | Private] Declare Sub name Lib "libname" [Alias "aliasname"] [([arglist])]
Syntax 2 - Funktionen
[Public | Private] Declare Function name Lib "libname" [Alias "aliasname"] [([arglist])] [As type]
Die optionalen Schlüsselwörter Public und Private geben den Bereich der importierten Funktion an: das gesamte Visual Basic-Projekt bzw. nur das Visual Basic-Modul. Der Name ist der Name, den Sie im VBA-Code verwenden möchten. Wenn dies vom Namen in der DLL abweicht, müssen Sie den Aliasnamen "Aliasname" verwenden, und Sie sollten den Namen der Funktion wie von der DLL exportiert geben. Wenn Sie auf eine DLL-Funktion zugreifen möchten, indem Sie auf eine DLL-Ordnungszahl verweisen, müssen Sie einen Aliasnamen angeben. Dabei handelt es sich um die Ordnungszahl mit dem Präfix von #.
Befehle m�ssen void zur�ckgeben. Funktionen m�ssen Typen zur�ckgeben, die VBA ByVal erkennen kann. Dies bedeutet, dass einige Typen einfacher zur�ckgegeben werden, wenn Argumente direkt ge�ndert werden: Zeichenfolgen, Arrays, benutzerdefinierte Typen und Objekte.
Hinweis
[!HINWEIS] VBA kann nicht �berpr�fen, ob die im Visual Basic-Modul angegebene Argumentliste und R�ckgabe identisch mit den in der DLL-Datei codierten Werten sind. Sie sollten dies selbst sehr sorgf�ltig pr�fen, da ein Fehler zum Absturz von Excel f�hren kann.
Wenn die Argumente der Funktion oder des Befehls nicht durch Verweis oder Zeiger �bergeben werden, muss ihnen das ByVal-Schl�sselwort in der arglist-Deklaration vorangestellt werden. Wenn eine C/C++-Funktion Zeigerargumente oder eine C++-Funktion Verweisargumente verwendet, m�ssen diese Funktionen ByRef �bergeben werden. Das ByRef-Schl�sselwort kann in Argumentlisten ausgelassen werden, da es der VBA-Standardwert ist.
Argumenttypen in C/C++ und VBA
Beachten Sie beim Vergleichen der Deklarationen von Argumenttypen in C/C++ und VBA die folgenden Punkte.
Ein VBA- String wird bei einer ByVal-Übergabe als Zeiger auf eine Byte-String-BSTR-Struktur übergeben, bei einer ByRef-Übergabe als Zeiger auf einen Zeiger.
Eine VBA- Variant, die eine Zeichenfolge enthält, wird bei einer ByVal-Übergabe als Zeiger auf eine Unicode-Breitzeichen-String-BSTR-Struktur übergeben, bei einer ByRef-Übergabe als Zeiger auf einen Zeiger.
Der VBA-Datentyp Integer ist eine 16-Bit-Typentsprechung des Datentyps "signed short" in C/C++.
Der VBA-Datentyp Long ist eine 32-Bit-Typentsprechung für den Datentyp "signed int" in C/C++.
Sowohl VBA als auch C/C++ unterstützen die Definition benutzerdefinierter Datentypen mit der Anweisung Type bzw. struct.
Der Datentyp Variant wird in VBA und C/C++ unterstützt; für C/C++ ist er in den Windows-OLE/COM-Headerdateien als VARIANT definiert.
VBA-Arrays sind OLE- SafeArrays, die für C/C++ in den Windows-OLE/COM-Headerdateien als SAFEARRAY sind.
Der VBA-Datentyp Currency wird bei einer ByVal-Übergabe als Struktur des Typs CY übergeben (definiert in der Windows-Headerdatei "wtypes.h") und bei einer ByRef-Übergabe als Zeiger darauf.
In VBA werden Datenelemente in benutzerdefinierten Datentypen für 4-Byte-Grenzen gepackt, in Visual Studio werden sie hingegen standardmäßig für 8-Byte-Grenzen gepackt. Daher müssen Sie die C/C++-Strukturdefinition in einen #pragma pack(4) … #pragma pack()
-Block einschließen, um eine Fehlausrichtung der Elemente zu vermeiden.
Nachfolgend finden Sie ein Beispiel für entsprechende Benutzertypdefinitionen.
Type VB_User_Type
i As Integer
d As Double
s As String
End Type
#pragma pack(4)
struct C_user_type
{
short iVal;
double dVal;
BSTR bstr; // VBA String type is a byte string
}
#pragma pack() // restore default
VBA unterstützt in einigen Fällen einen größeren Wertebereich als Excel unterstützt. Der VBA-Double ist IEEE-kompatibel und unterstützt unternormale Zahlen, die derzeit auf dem Arbeitsblatt auf 0 (null) gerundet sind. Der VBA-Datumstyp kann Datumsangaben bereits am 1. Januar 0100 mit negativen serialisierten Datumsangaben darstellen. Excel lässt nur serialisierte Datumsangaben zu, die größer oder gleich 0 (null) sind. Der VBA-Währungstyp – eine skalierte 64-Bit-Ganzzahl – kann eine Genauigkeit erreichen, die in 8-Byte-Doubles nicht unterstützt wird und daher im Arbeitsblatt nicht abgeglichen wird.
Excel übergibt nur Variant-Werte der folgenden Typen an eine benutzerdefinierte VBA-Funktion.
VBA-Datentyp | Bit-Flags für C/C++-Variant-Typ | Beschreibung |
---|---|---|
Gleitkommawert mit doppelter Genauigkeit |
VT_R8 |
|
Boolean |
VT_BOOL |
|
Datum |
VT_DATE |
|
Zeichenfolge |
VT_BSTR |
OLE-Bstr-Byte-String |
Range |
VT_DISPATCH |
Bereichs- und Zellbezüge |
Variant-Wert mit einem Array |
VT_ARRAYVT_VARIANT |
Literale Arrays |
Ccy |
VT_CY |
64-Bit-Ganzzahl, skaliert für eine Genauigkeit von 4 Dezimalstellen |
Variant-Wert mit einem Fehler |
VT_ERROR |
|
VT_EMPTY |
Leere Zellen oder ausgelassenen Argumente |
Sie k�nnen den Typ eines �bergebenen Variant-Werts in VBA mithilfe von VarType �berpr�fen, allerdings gibt die Funktion den Typ der Bereichswerte zur�ck, wenn sie mit Verweisen aufgerufen wird. Um zu ermitteln, ob ein Variant-Wert ein Range-Verweisobjekt ist, k�nnen Sie die IsObject-Funktion verwenden.
Sie k�nnen Variants mit Variantenarrays in VBA aus einem Range erstellen, indem Sie die zugeh�rige Value-Eigenschaft einem Variant-Wert zuweisen. Alle Zellen im Quellbereich, die mit dem Standardw�hrungsformat aus den zu diesem Zeitpunkt g�ltigen Landes-/Regionaleinstellungen formatiert sind, werden in Arrayelemente des Typ Currency konvertiert. Alle als Datumsangaben formatierten Zellen werden in Arrayelemente des Typs Date konvertiert. Zellen mit Zeichenfolgen werden in Breitzeichen-BSTR-Variant-Werte konvertiert. Zellen mit Fehlern werden in Variants des Typs VT_ERROR konvertiert. Zellen mit BooleanTrue oder False werden in Variants des Typs VT_BOOL konvertiert.
Hinweis
Variant speichertTrue als -1 und False als 0. Zahlen, die nicht als Datumsangaben oder Währungsbeträge formatiert sind, werden in Variant-Werte des Typs VT_R8 konvertiert.
Variant- und String-Argumente
Excel arbeitet intern mit Breitzeichen-Unicode-Strings. Wenn die Deklaration einer benutzerdefinierten VBA-Funktion die Verwendung eines String-Arguments angibt, konvertiert Excel die angegebene Zeichenfolge mit einer für das jeweilige Gebietsschema spezifischen Methode in einen Byte-String-Wert. Wenn die Funktion als Unicode-String übergeben werden soll, muss die benutzerdefinierte VBA-Funktion ein Variant- anstelle eines String-Arguments verwenden. Die DLL-Funktion kann dann diesen Variant-BSTR-Breitzeichen-String von VBA akzeptieren.
Um Unicode-Strings als einer DLL an VBA zurückzugeben, müssen Sie ein Variant-String-Argument direkt bearbeiten. Damit dies funktioniert, müssen Sie die DLL-Funktion deklarieren, indem Sie einen Zeiger auf den Variant-Code und in Ihrem C/C++-Code verwendet, und das Argument im VBA-Code als ByRef varg As Variant
deklarieren. Der alte Zeichenfolgenspeicher muss gelöscht werden, und der neue Zeichenfolgenwert muss unter Verwendung de OLE-Bstr-String-Funktionen nur in der DLL erstellt werden.
Um einen Byte-String aus einer DLL an VBA zurückzugeben, müssen Sie ein Byte-String-BSTR-Argument direkt bearbeiten. Damit dies funktioniert, müssen Sie die DLL-Funktion deklarieren, um einen Zeiger auf einen Zeiger auf den BSTR und in Ihrem C/C++-Code zu verwenden, und das Argument im VBA-Code als " ByRef varg As String" deklarieren.
Sie sollten nur Zeichenfolgen, die mit diesen Methoden von VBA übergeben werden, mit den OLE-BSTR-String-Funktionen bearbeiten, um Speicherprobleme zu vermeiden. Sie müssen beispielsweise SysFreeString aufrufen, um den Speicher freizugeben, bevor Sie die übergebene Zeichenfolge überschreiben, und SysAllocStringByteLen oder SysAllocStringLen, um Speicher für eine neue Zeichenfolge zuzuordnen.
Sie können Excel-Arbeitsblattfehler als Variants in VBA erstellen, indem Sie die CVerr-Funktion mit den in der folgenden Tabelle aufgeführten Argumenten verwenden. Arbeitsblattfehler können auch mit Variants vom Typ VT_ERROR und mit den folgenden Werten im ulVal-Feld von einer DLL an VBA zurückgegeben werden.
Fehler | UlVal-Wert der Variante | CVerr-Argument |
---|---|---|
#NULL! |
2148141008 |
2000 |
#DIV/0! |
2148141015 |
2007 |
#VALUE! |
2148141023 |
2015 |
#REF! |
2148141031 |
2023 |
#NAME? |
2148141037 |
2029 |
#NUM! |
2148141044 |
2036 |
#N/A |
2148141050 |
2042 |
Beachten Sie, dass der angegebene Variantwert ulVal dem Wert des CVerr-Arguments plus dem Hexadezimalwert x800A0000 entspricht.
Direktes Aufrufen von DLL-Funktionen aus dem Arbeitsblatt
Sie können aus dem Arbeitsblatt nicht auf Win32-DLL-Funktionen zugreifen, ohne beispielsweise VBA oder XLM als Schnittstelle zu verwenden oder ohne Excel vorab Angaben zur Funktion, zu deren Argumenten und zum Rückgabetyp zu machen. Der erforderliche Prozess wird als Registrierung bezeichnet.
Der Zugriff auf die Funktionen einer DLL kann in einem Arbeitsblatt mit den folgenden Methoden erfolgen:
Deklarieren der Funktion in VBA wie zuvor beschrieben und Zugreifen auf die Funktion über eine benutzerdefinierte VBA-Funktion
Aufrufen der DLL-Funktion durch Ausführen von CALL für eine XLM-Makrovorlage und Zugreifen auf die Funktion über eine benutzerdefinierte XLM-Funktion
Verwenden eines XLM- oder VBA-Befehls zum Aufrufen der XLM-Funktion REGISTER, die die Informationen bereitstellt, die Excel zum Erkennen der Funktion benötigt, wenn diese in eine Arbeitsblattzelle eingegeben wird
Umwandeln der DLL in eine XLL und Registrieren der Funktion mithilfe der C-API-Funktion xlfRegister beim Aktivieren der XLL
Die vierte Methode ist eigenständig: Der Code, der die Funktionen registriert, und der Funktionscode sind beide im gleichen Codeprojekt enthalten. Am Add-In vorgenommene Änderungen erfordern keine Änderungen an einem XLM-Blatt oder einem VBA-Codemodul. Um dies auf gut verwaltete Weise durchzuführen, ohne den Funktionsumfang der C-API zu verlassen, müssen Sie die DLL in eine XLL umwandeln und das resultierende Add-In mithilfe des Add-In-Managers laden. Dies ermöglicht es Excel, eine Funktion aufzurufen, die die DLL verfügbar macht, wenn das Add-In geladen oder aktiviert wird; von dort aus können Sie alle Funktionen der XLL registrieren und andere DLL-Initialisierungen ausführen.
Direktes Aufrufen von DLL-Befehlen aus Excel
Auf Win32-DLL-Befehle kann nicht direkt aus Excel-Dialogfeldern und -Menüs zugegriffen werden, ohne dass eine Schnittstelle wie z. B. VBA vorhanden ist oder die Befehle vorab registriert werden.
Auf die Befehle einer DLL kann mit folgenden Methoden zugegriffen werden:
Deklarieren des Befehls in VBA wie zuvor beschrieben und Zugreifen auf die Funktion über ein VBA-Makro
Aufrufen des DLL-Befehls durch Ausführen von CALL für eine XLM-Makrovorlage und Zugreifen auf die Funktion über ein XLM-Makro
Verwenden eines XLM- oder VBA-Befehls zum Aufrufen der XLM-Funktion REGISTER, die die Informationen bereitstellt, die Excel zum Erkennen des Befehls benötigt, wenn dieser in ein Dialogfeld eingegeben wird, das den Namen eines Makrobefehls erwartet
Umwandeln der DLL in eine XLL und Registrieren des Befehls mit der C-API-Funktion xlfRegister
Wie bereits weiter oben im Kontext von DLL-Funktionen erläutert, ist die vierte Methode die eigenständigste, und Registrierungscode und Befehlscode bleiben hier nahe beieinander. Hierzu müssen Sie die DLL in eine XLL umwandeln und das resultierende Add-In mithilfe des Add-In-Managers laden. Wenn Sie Befehle auf diese Weise registrieren, haben Sie außerdem die Möglichkeit, den Befehl an ein Element der Benutzeroberfläche anzufügen, z. B. ein benutzerdefiniertes Menü, oder ein Ereignistrap einzurichten, das den Befehl bei einem bestimmten Tastaturanschlag oder einem anderen Ereignis aufruft.
Für alle bei Excel registrierten XLL-Befehle wird von Excel die folgende Form vorausgesetzt.
int WINAPI my_xll_cmd(void)
{
// Function code...
return 1;
}
Hinweis
Der Rückgabewert wird von Excel ignoriert, es sei denn, der Aufruf erfolgt aus einer XLM-Makrovorlage; in diesem Fall wird der Rückgabewert in TRUE oder FALSE konvertiert. Sie müssen daher 1 zurückgeben, wenn der Befehl erfolgreich ausgeführt wurde, und 0 bei einem Fehler oder Benutzerabbruch.
DLL-Speicher und mehrere DLL-Instanzen
Wenn eine Anwendung eine DLL lädt, wird der ausführbare Code der DLL in den globalen Heap geladen, sodass sie ausgeführt werden kann, und auf dem globalen Heap wird Speicherplatz für ihre Datenstrukturen zugewiesen. Windows verwendet die Speicherzuordnung, um diese Speicherbereiche so erscheinen zu lassen, als ob sie sich im Prozess der Anwendung befinden, sodass die Anwendung darauf zugreifen kann.
Wenn anschließend eine zweite Anwendung die DLL lädt, erstellt Windows keine weitere Kopie des ausführbaren DLL-Codes, da der Speicher schreibgeschützt ist. Windows ordnet den Speicher des ausführbaren DLL-Codes den Prozessen beider Anwendungen zu. Sie weist jedoch einen zweiten Speicherplatz für eine private Kopie der Datenstrukturen der DLL zu und ordnet diese Kopie nur dem zweiten Prozess zu. Dadurch wird sichergestellt, dass keine Anwendung einen Konflikt mit den DLL-Daten der anderen Anwendung verursachen kann.
Dies bedeutet, dass sich DLL-Entwickler keine Gedanken darüber machen müssen, dass mehrere Anwendungen oder mehrere Instanzen der gleichen Anwendung auf statische und globale Variablen und Datenstrukturen zugreifen. Jede Instanz jeder Anwendung erhält eine eigene Kopie der Dll-Daten.
DLL-Entwickler müssen sich Gedanken darüber machen, dass dieselbe Instanz einer Anwendung ihre DLL mehrmals aus verschiedenen Threads aufruft, da dies zu Konflikten bei den Daten dieser Instanz führen kann. Weitere Informationen finden Sie unter Speicherverwaltung in Excel.