Funktion in DLL exportieren und in Excel nutzen



  • Hi,

    ich vermute gerade am ehesten, dass mein Fehler bei MSVC++ liegt, daher poste ich hier.

    Also ich habe MS Visual Studio C++ 2011 und würde gerne eine DLL exportieren und dann in Excel importieren und nutzen. Ich weiß leider nicht, wie ich das genau anstelle. Google gab mir ein paar Codebeispiele, aber leider funktioniert das bei mir nicht.

    Also im C++ Source habe ich jetzt Mal ganz doof das hier geschrieben:

    __declspec(dllexport) double doSomething()
    {
    	return 15.5;
    }
    

    Oft wurde auch noch dieses __stdcall mit dazu geschrieben in den Beispielcodes. Wenn ich das einsetze und erneut kompiliere ändert das aber nichts.

    Ich habe am Anfang einfach ein neues Projekt aufgemacht und es dann nachher auf den Konfigurationstyp "Dynamische Bibliothek (.dll)" gestellt. Sonst habe ich nichts geändert.

    Die erstellte DLL habe ich dann einfach Mal auf C:\ verschoben.

    Und der VBA-Teil in Excel sieht jetzt so aus:

    Declare Function doSomething Lib "C:\DllTest.dll" () As Double
    
    Function Test() As Double
    
    Test = doSomething
    
    End Function
    

    Habe statt .dll auch Mal das .dll weggelassen, weil das manche auch in einigen Codes taten, ohne Erfolg. Wenn ich die Funktion aufrufe, erhalte ich in der jeweiligen Zelle "#WERT" und wenn ich Mal MsgBox doSomething schreibe, erzeugt er mir auch keine MsgBox. 😕

    Irgendwelche Ideen?



  • Hi,

    Problem gelöst. Man sollte wohl zur Verwendung in Excel statt declspec(__dllexport) eine .def-Datei verwenden. Dann müssen die Funktionen alle noch mit extern "C" und __stdcall versehen werden. Wie das geht, steht hier ganz gut beschrieben:

    http://www.doxapp.com/code/excel-dll.html

    Natürlich erklärt mir das noch nicht, wie man Klassen verwendet o.ä. Falls dazu jemand Ideen hat, immer gern!

    Okay, interessant ist auch dumpbin! Das ist bei MSVC dabei und mit "dumpbin /exports bla.dll" kann man sich anzeigen lassen, was genau exportiert wird. Und MSVC macht ohne das 'extern "C"' recht kryptisch. Ich vermute, wenn VBA nicht über den Namen beginnend mit ? meckern würde, könnte man das auch unter dem kryptischen Namen nutzen.

    Ich frage mich nur, ob Klassen dann funktionieren...



  • Ok, nächstes Problem.

    Wie übergebe ich einen String? In den Beispielen, bei denen die Windows-DLLs aufgerufen werden, werden die Argumente einfach als String deklariert. Und in MSVC sind die LPCSTR. Also habe ich auch Mal eine Funktion mit LPCSTR genutzt und versucht das Resultat auszugeben (über Msgbox und über std::ofstream). Aber ich erhalte meist nur den ersten Buchstaben oder irgend einen Datensalat. Jemand ne Idee? 😕



  • Eisflamme schrieb:

    Aber ich erhalte meist nur den ersten Buchstaben oder irgend einen Datensalat. Jemand ne Idee? 😕

    Klingt nach ANSI vs. UNICODE-Problemen.



  • Eisflamme schrieb:

    Problem gelöst. Man sollte wohl zur Verwendung in Excel statt declspec(__dllexport) eine .def-Datei verwenden. Dann müssen die Funktionen alle noch mit extern "C" und __stdcall versehen werden.

    Exakt. Wobei die .def Datei eigentlich nicht unbedingt notwendig sein sollte, extern "C" und __stdcall sind auf jeden Fall nowendig (außer du kompilierst mit /Gz, dann ist natürlich nur extern "C" unbedingt notwendig).

    Eisflamme schrieb:

    Natürlich erklärt mir das noch nicht, wie man Klassen verwendet o.ä. [...]

    Gar nicht. dlls kennen keine Klassen. In VB (Classic) sind "Klassen" afaik aber eigentlich COM Interfaces, d.h. wenn du "Klassen" willst dann läuft das über COM.

    Eisflamme schrieb:

    Okay, interessant ist auch dumpbin! Das ist bei MSVC dabei und mit "dumpbin /exports bla.dll" kann man sich anzeigen lassen, was genau exportiert wird. Und MSVC macht ohne das 'extern "C"' recht kryptisch. Ich vermute, wenn VBA nicht über den Namen beginnend mit ? meckern würde, könnte man das auch unter dem kryptischen Namen nutzen.

    Stichwort Name-Mangling. Genau darum ist auch das extern "C" notwendig. Mit dem Schlüsselwort Alias könntest du theoretisch Funktionen mit jedem beliebigen Namen nutzen:

    Declare Function doSomething Lib "C:\DllTest.dll" Alias "blabliblo" () As Double
    


  • Ok, freut mich, dass meine Erkenntnisse bestätigt wurden. 🙂

    Ich habe das Problem eingegrenzt. 1. muss man in Excel byValue übergeben, da man sonst nur einen Zeiger auf den Speicherbereich kriegt und das nicht zu klappen scheint und 2. wird jeder einzelne Char nullterminiert...

    D.h. in Excel übergebe ich "AB" und in C++ kommt "A\0B\0" an... Komisch. Die Windows-DLLs müssen doch auch einen vernünftigen String erhalten?



  • Wie ich schon sagte: UNICODE - was du da bekommst, dürfte eigentlich ein wide-String mit dem Inhalt L"AB" sein, der nur von deinem Programm als normaler String interpretiert wird.



  • Sacrebleu! Vorhin hat das Umstellen auf wchar_t nichts bewirkt, nun geht's, danke. 🙂 Darf ich davon ausgehen, dass das immer UNICODE ist oder muss ich irgendwie eine Fallunterscheidung einbauen? Soll ja auf unterschiedlichen Systemen laufen. Aber einmal kompiliert dürfte für die DLL UNICODE ja fix sein, richtig?



  • Verwend einfach TCHAR, dann kannst dus ohne Probleme für beide Varianten kompilieren 😉



  • Zum Arbeiten muss ich es dann aber doch wieder konvertieren, oder?



  • Was genau meinst du damit?



  • Na ja, ich muss die Zeichen ja interpretieren, d.h. auch Vergleichen. Es ist schön, dass TCHAR automatisch der richtige Typ ist, aber es steckt ein char oder ein wchar_t dahinter und je nachdem, was es ist, muss ich doch wieder andere Stringoperationen nutzen. Zum Arbeiten will ich es ja lieber in einem std::string haben und nicht auf den Charketten rumhantieren. Einen wchar_t* muss ich aber erst konvertieren, um einen string zu haben, einen char* eben nicht.

    Frage 2:
    Kann sich meine DLL bei Nutzung von Excel nichts merken? Ich berechne eine Tabelle vor, diesen Schritt würde ich gerne nur einmal ausführen. Und meine Funktion speichert die Tabelle auch und sollte beim zweiten Aufruf eigentlich nicht mehr die Tabelle neu erzeugen. Aber bei jedem Funktionsaufruf scheint Excel die DLL neu zu laden. Kann ich das Speichern irgendwie gewährleisten?



  • Zum Glück waren die Leute bei Microsoft so clever, die meisten dieser Unterschiede auch zu verpacken, indem sie entsprechende TCHAR-abhängige Funktionen bereitstellen, z.B. _tcscmp() für Vergleiche.
    (oder du verwendest ein typedef basic_string<TCHAR> tstring; )



  • Eisflamme schrieb:

    Na ja, ich muss die Zeichen ja interpretieren, d.h. auch Vergleichen. Es ist schön, dass TCHAR automatisch der richtige Typ ist, aber es steckt ein char oder ein wchar_t dahinter und je nachdem, was es ist, muss ich doch wieder andere Stringoperationen nutzen. Zum Arbeiten will ich es ja lieber in einem std::string haben und nicht auf den Charketten rumhantieren. Einen wchar_t* muss ich aber erst konvertieren, um einen string zu haben, einen char* eben nicht.

    Wie wärs mit

    std::basic_string<TCHAR>
    

    😉

    Eisflamme schrieb:

    Kann sich meine DLL bei Nutzung von Excel nichts merken? Ich berechne eine Tabelle vor, diesen Schritt würde ich gerne nur einmal ausführen. Und meine Funktion speichert die Tabelle auch und sollte beim zweiten Aufruf eigentlich nicht mehr die Tabelle neu erzeugen. Aber bei jedem Funktionsaufruf scheint Excel die DLL neu zu laden. Kann ich das Speichern irgendwie gewährleisten?

    Ich hab ehrlich gesagt keine Erfahrung mit diesen Dingen in Office, aber wenn Excel das so macht dann macht Excel das eben so, ich denk nicht dass du da viel tun kannst. Merk dir die Tabelle eben in einer Datei wenns nicht anders geht?


  • Mod

    Und... ? Dann definiere Dir tstring 😉

    typedef basic_string<TCHAR> tsring;
    

    Zudem gibt es X-Makros (Funktionen), die die Umwandlung einfach machen.

    CT2A, CT2W, CW2T, CA2T
    

    etc.

    Warum string benutzen, wenn in Windows fast alles auf wchar_t aufbaut.



  • Na ja, mein ganzes Programm basiert auf Strings. Nur, weil ich für diese DLL-Funktion jetzt wchars nutzen will, will ich ja nicht alles umbauen. Da konvertier ich lieber einmal und arbeite weiter damit. Aber die Vorschläge sind gut, danke. 🙂

    Jetzt habe ich wieder eine neue Frage:
    Ich möchte meine DLL möglichst einfach für die Benutzer in Excel verfügbar machen. Die Pfadangabe ist aber absolut. Wenn ich mit regsvr32 die DLL registrieren möchte, meckert der Service, dass der DLL-Einstiegspunkt nicht gefunden werden kann.

    Wie kriege ich diesen DLL-Einstiegspunkt in meine DLL? Ich habe jetzt DllRegister und DllUnregister gefunden, aber wenn ich die leerlasse und exportiere, meckert regsvr32 zwar nicht (sagt sogar, war erfolgreich), aber obv. ist die DLL dann trotzdem nicht wirklich bekannt. Aber ich habe keine Ahnung, was ich da für einen Inhalt reinstecken soll 😕

    Ok, habe ein paar Beispielcodes gefunden, die aber nicht sehr transparent waren. Lieber wäre mir so eine .reg-Datei, die der Benutzer dann einfach ausführen muss, um die DLL zu registrieren. Hat jemand einen Link/Tipp, wie ich so eine bauen kann?


  • Mod

    Eine REG Datei ist der schlechte Weg, denn dann muss die DLL genau in dem Pfad liegen in dem die Reg-Datei es vorschreibt.

    Bleib bei der REGSVR32 Variante, dass der Weg, den COM für ActiveX Controls vorsiehtund die auch jeder Admin erwarten würde.



  • Okay, danke. 🙂



  • Naja, ich vermute mal das Problem ist dass deine dll eben kein COM Server ist. Aber regsvr32 kann afaik nicht nur COM Server registrieren sondern auch "normale" dlls. Schau dir mal die Parameter von regsvr32 an, iirc war da was...



  • Hab nachgeschaut. Hab nur die Wahl zwischen DLLInstall und DLLRegisterServer...


Log in to reply