Laden...

C++ DLL für C# erstellen (DllImport)

Erstellt von JunkyXL vor 14 Jahren Letzter Beitrag vor 14 Jahren 8.162 Views
JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren
C++ DLL für C# erstellen (DllImport)

Hallo,

jetzt brauch ich doch mal eure Hilfe. Ich muss eine Lizenzschlüssel Generierungsmethode in eine C++ DLL auslagen.
Ganz normales natives C++. Grund ist einfach nur der Reflector, mit dem der Code ausgelesen werden kann. Also soll ich das Ganze nach C++ portieren.

Die Funktion soll aus C# heraus aufrufbar sein (DLLImport).

Habe nun das Ganze kompiliert, aber habe als Ergebnis nicht wirklich eine DLL.
Was tun?
Muss ich noch etwas besonderes beachten, wenn ich Funktionen aus anderen Anwendungen aufrufbar machen will?

S
248 Beiträge seit 2008
vor 14 Jahren

Hallo JunkyXL,

am besten gehst du so vor:

Neues Projekt anlegen (Visual C++ -> Win32-Projekt) mit Anwendungstyp "Dll". Andere Optionen lassen, wie sie sind.

In der erzeugten .cpp Datei kannst du nun zB eine Funktion so deklarieren:


// Gehört eigentlich in die .h Datei
extern "C"
{
	BOOL __declspec(dllexport) __stdcall CheckKey(const unsigned char *key);
}

BOOL __stdcall InternalHelper(const unsigned char *key);
// Ende .h Datei

// Kann von aussen aufgerufen
BOOL __stdcall CheckKey(const unsigned char *key) {
	return InternalHelper(key);
}

// Nur intern verwendbar
BOOL __stdcall InternalHelper(const unsigned char *key) {
	return FALSE;
}

Dabei ist zu beachten, dass Funktionen die mit __declspec(dllexport) deklariert sind, nach aussen exportiert werden (d.h. Hilfsmethoden ohne dieses Tag können auch von aussen nicht aufgerufen werden).

Spooky

JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren

Danke, das ist ja schon mal was.

Da die Funktion einen string zurückgeben muss (Keygenerierung), habe ich das mal wie folgt geändert:

#include "stdafx.h"
#include "stdio.h"

using namespace std;

// Gehört eigentlich in die .h Datei
extern "C"
{
	string __declspec(dllexport) __stdcall GenerateKey(string key);
}

string __stdcall InternalHelper(string key);
// Ende .h Datei

// Kann von aussen aufgerufen
string __stdcall GenerateKey(string key)
{
	return InternalHelper(key);
}

// Nur intern verwendbar
string __stdcall InternalHelper(string key)
{
	return "test";
}

Habe es auch mit const unsigned char anstatt string probiert.
Bekomme beim Kompilieren aber lauter Fehler (Bild angehängt).

Die Methode soll folgende Signatur haben:

string Generate(string s1, string s2, string s3, int number, DateTime date);

DateTime kann zur Not auch ein string sein.
Wie würde das konkret in C++ aussehen?

1.361 Beiträge seit 2007
vor 14 Jahren

Hi Junky,

wenn du nicht "#include <string>" machst, kannst du auch kein string verwenden.
Aber ich würde eh dazu tendieren, alles auf char* aufzubauen.

Die C-Signatur wäre dann


char* Generate(char* s1, char* s2, char* s3, int number, double date);

beste Grüße
zommi

JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren

Danke zommi,

wenn ich deine char* Variante auskommentiere, lässt es sich kompilieren.
Ansonsten bekomme ich folgenden Fehler:

[1] Warning C4190: 'GenerateKey' has C-linkage specified, but returns UDT 'std::basic_string<_Elem,_Traits,_Ax>' which is incompatible with C (line 10)
[2] Error C2059: syntax error : '__declspec(dllexport)' (line 12)

#include "stdafx.h"
#include "stdio.h"
#include "string"

using namespace std;

// Header
extern "C"
{
	string __declspec(dllexport) __stdcall GenerateKey(string s1, string s2, string s3, int number, double date);

	//char* __declspec(dllexport) __stdcall GenerateKey(char* s1, char* s2, char* s3, int number, double date);
}


// CPP
string __stdcall GenerateKey(string s1, string s2, string s3, int number, double date)
{
	return "test";
}

//char* __stdcall GenerateKey(char* s1, char* s2, char* s3, int number, double date)
//{
//	return "test";
//}

Eine Frage bleibt noch unabhängig von dem Fehler: Wo ist nun die DLL, die ich mit der kompilierbaren Version erhalten müsste? Habe im Debug/Release Ordner nur die Dateien, die ich oben im Screenshot bereits gezeigt habe.

1.361 Beiträge seit 2007
vor 14 Jahren

Tja, wie schon erwähnt, arbeite mit char*s.

Die DLL-Exporte markierst du ja als extern "C".
Damit sagst du deinem C++-Compiler, dass das C-Code ist,
allerdings ist

stdbasic_string<_Elem,_Traits,_Ax>' [...] incompatible with C .
std
string ist eben C++.

Wo ist nun die DLL

Kann sein, dass die DLL dann woanders abgelegt wird.
(Beispielsweise im obersten Ordner (Der solution) unter Debug/Release, und nicht in den Unter-Projektordnern) Schau in den Projekteinstellungen nach. (Oder nutz die DateiSuche 😉)

beste Grüße
zommi

S
248 Beiträge seit 2008
vor 14 Jahren

DateTime ist intern ein .NET long. Der C++ Typ time_t (time.h), der intern ebenfalls ein 64bit Integer ist könnte dafür ggf verwerdendet werden. Time Beispiel

Wie zommi schon gesagt hat, ist char * der Typ den du verwenden solltest. Ggf kannst du auch wchar_t * (Unicode) nehmen:


#include <time.h>
LPCWSTR __stdcall Generate(LPCWSTR s1, LPCWSTR s2, LPCWSTR s3, int number, time_t time);

JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren

Hab jetzt einmal LPCWSTR und char* verwendet.
Ohne Probleme kompiliert, DLL ins Verzeichnis der Aufrufenden Anwendung und mit [DllImport] die Funktion zugänglich machen.
Bekomme nun eine

EntryPointNotFoundException: Unable to find an entry point named 'GenerateKey' in DLL 'LicenseKeyGenerator.dll'.

Habe mal die Solution angehängt. Wäre nett, wenn sie jemand von euch anschauen könnte.

1.361 Beiträge seit 2007
vor 14 Jahren

Hi Junky,

  1. wenn die Methoden nicht als "dllExport" gekennzeichnet werden, dann kann man die auch nicht von außen aufrufen. (son bissl wie public / private)

Du musst also die betreffenden Methoden mit

extern "C" __declspec(dllexport) ... 

versehen. (wie dus oben auch schon hattest)

  1. Die Methoden, die du in der Header Datei hast, haben nix mit denen in der cpp-Datei zu tun. Das eine sind globale Funktionen und das andere Methoden einer Klasse.
    Dass du die gleich benannt hast, stört jedoch den Compiler nicht. Trotzdem war das sicher nicht dein Ziel 😃

  2. Du brauchst auch den ganzen Overhead mit der DLLMain und den zusätzlichen Dateien nicht. Du musst keine Objekte initialisieren. Das einzige was du brauchst, ist eine c/cpp Datei mit dem Inhalt:


#include <time.h>
//optionale weitere header

extern "C" __declspec(dllexport) char* GenerateKey(char* s1, char* s2, char* s3, int number, time_t time)
{
	//hier kommt dein Code rein//
	return NULL;
}

Erstell dir am besten ein leeres Projekt. Füge diese Datei hinzu, und stell in der Konfiguration von EXE auf DLL.

beste Grüße
zommi

PS: Eventuell muss auf C# Seite dein Rückgabetyp ein StringBuilder sein, dass weiß ich grad nich genau.
//Edit: ne, klappt auch so !

S
248 Beiträge seit 2008
vor 14 Jahren

Hallo,

ich hab das Projekt mal umgebaut. Die .dll muss nurnoch ins Ausgabe-Verzeichnis des .NET Projektes kopiert werden.

Spooky

PS: StringBuilder braucht man wenn man einen Buffer übergibt in den geschrieben werden soll. Diese Funktionen haben dann meistens noch einen Parameter, der die Bufferlänge angibt zb:
void FillMe(char * buffer, int length, ...);
->
void FillMe(StringBuilder b, int length, ...);

Wenn es um fixe Strings geht braucht man keinen StringBuilder anlegen.

edit:
Der .NET Typ DateTime und der C++ Typ time_t sind wohl doch nicht kompatibel, daher musst du wohl einen kompatiblen Typen suchen oder Hilfsmethoden implementieren.

1.361 Beiträge seit 2007
vor 14 Jahren

Hi nochmal,

hier nochmal ein Bild, wie die Minimalkonfiguration aussehen muss.
Und da die DLL standardmäßig ins falsche Verzeichnis kommt, ist das auch noch angepasst.

$(SolutionDir)$(ConfigurationName)

wurde zu

$(SolutionDir)\TestApplication\bin\$(ConfigurationName)

beste Grüße
zommi

//Edit: zu langsam 😃

JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren

Danke für eure Bemühungen, hatte es mittlerweile schon rausgefunden 🙂
Habe jetzt einfach nur im Header File ein extern "C" { } um die Deklarationen geschrieben, was ich dachte schon so gehabt zu haben 🤔

Naja egal, es funktioniert. Jetzt muss ich nur noch die Fuktionalität implementieren. Das gibt auch wieder was.

Nochmals Danke an euch!

PS: danke auch für den Tipp mit dem Ausgabeverzeichnis. Das war der Grund, warum ich die DLL auch nicht auf Anhieb gefunden habe.

1.361 Beiträge seit 2007
vor 14 Jahren

Hi Junky,

mal ein ganz anderer Gedanke. Du mühst dich jetzt ja ab, alles in c/c++ zu übertragen, damit der Code nichtmehr als MSIL sondern in Maschinencode vorliegt.

Warum bleibst du nicht in C# und wandelst die KeyGenerator-Bibliothek mit Ngen um ?! Dann sollte der Code doch auch im Maschinencode vorliegen und niemand kann mit ILDASM was anfangen.
Bei MSIL Code verschleiern wurde sowas angedeutet, aber dann wieder verworfen ?!

beste Grüße
zommi