Was muss inkludiert sein...?

ManicMarble

Erfahrenes Mitglied
Hallo liebe C- und API-Experten,
leider kann ich mich selbst nicht als ein solcher bezeichnen - ganz im Gegenteil. Jedesmal wenn ich mich mit sowas rumschlagen muss, ist das für mich der blanke Horror... deshalb bitte ich verzweifelt um Hilfe bei diesem hier:
PHP:
DWORD cStr2oStr(char *cStr) {
	
	HANDLE  oBuffer;
	char   *oStr;
	DWORD   dwOStr = MAKELONG(0, 1);
	size_t  len = 0;
	
	if (cStr) len = strlen(cStr);
	
	oBuffer = GlobalAlloc(GMEM_MOVEABLE, (DWORD)len + 1);
	
	if (oBuffer) {
		oStr = GlobalLock(oBuffer);
		if (cStr) memcpy(oStr, cStr, len + 1);
		GlobalUnlock(oBuffer);
		dwOStr = MAKELONG(oBuffer, 0);
	}
	
	return (dwOStr);
}
Diese Funktion stammt aus dem Sourcecode einer in Visual-C++ 2.0 geschriebenen DLL. Sie dient zum Export von C-Strings (char) an eine recht eigenwillige, VB4-ähnliche Entwicklungsumgebung. Immer wenn aus einer zu exportierenden Funktion ein String zurückgegeben werden soll, wird das Ergebnis vom Typ char zuerst an diese Funktion übergeben und der Rückgabewert (DWORD) wird exportiert.

Was die Funktion so im Groben veranstaltet verstehe ich eigentlich schon: Es wird ein Speicherbereich alloziiert, dann gesperrt, dann wird der Inhalt aus dem Speicherbereich der char-Variable in diesen neuen Bereich kopiert, dann wird der wieder entsperrt und ein Zeiger zu diesem wird zurückgegeben. Korrigiert mich, wenn ich da voll daneben liege.

So. Ich muss sowas jetzt nach-programmieren in VC++ 6 und ich bin wie gesagt werder ein C- noch ein API-Held. Wenn's ans Speicherbereiche-Schubsen geht, dann geh' ich normalerweise heim ;). Also frag' ich mal euch:

1. Welche Header-Dateien müssen included werden? GlobalAlloc & Co. kommen doch aus der Win-API, oder? Was ist mit strlen() und memcpy(), wo kommen diese Funktionen her? Und MAKELONG() - ist das ein Makro? Wo kommt denn das wieder her?

2. Die MSDN-Library schreibt zu GlobalLock und Konsorten: "This function is provided only for compatibility with 16-bit versions of Windows". Ah so. 16-bit Windows muss nun wirklich nicht mehr sein, meine eigene DLL muss unter Win'98 und neuer laufen. Was sind die zeitgemäßen Äquivalente dafür?

3. Kann mir nicht einer schnell mal diese Funktion so umpfriemeln, dass ich sie in VC++ 6 copypaste und gut? Da wär ich echt super glücklich drüber. Werde mich auch revanchieren und midestens 10 Fragen hier im MySQL-Forum beantworten...;)

4. Oder noch besser: weiß jemand vielleicht eine bessere Lösung, wie ich nullterminierte Strings vom Typ char bzw. LPCTSTR bzw. CString aus einer C-DLL an eine superexotische, VB4-artige Programmiersprache exportieren kann, die mit Pointern nix anfangen kann und deren Strings "kopfgesteuert" sind?

Über schnelle Hilfe freut sich
Martin
 
Zuletzt bearbeitet:
1) GlobalAlloc sind windows.h, ebenso das MAKELONG-Makro. strlen und memcpy müssten über stdio.h bzw. cstdio (ohne .h) erreichbar sein.

2) GlobalLock kannst du aber trotzdem verwenden, dann bekommst du nämlich den Pointer auf den alloziierten Speicher zurück.

3) Ich würde mich ehrlich gesagt wundern, wenn da etwas umgebaut werden müsste. Der Code sieht nämlich astrein aus. Der müsste genau so sauber kompilieren

4) Die Funktion gibt im Prinzip schon null-terminierte Strings zurück. Der Unterschied ist nur, dass der Speicher nicht aus dem lokale Heap stammt sondern aus dem globalen. Auf diesen können alle zugreifen bzw. Windows mappt das sauber um. Wenn du direkt deinen Pointer weitergeben würdest, kann das böse ins Auge gehen. Es kommt eben darauf an, was das Zielprogramm mit dem Pointer macht.
 
Oh, da war ich doch gerade selber am schreiben!
Aber ich sehe das genauso wie Endurion. Eigentlich ist alles OK.
Es kommt halt bloss darauf an, was deine DLL für Pointer zurückgeben soll.
 
Hey, vielen Dank!
Das hilft mir bestimmt schon weiter. Hatte bisher stdio.h nicht includiert. Kanns allerdings erst morgen testen, bin inzwischen zu Hause und das Projekt ist "beruflich", hab's also nicht hier.

Zu 3.:
Dass der Code der Funktion in sich korrekt ist war schon klar, schließlich funktioniert er ja in der DLL aus der ich ihn rauskopiert habe. Nur in meinem neuen DLL-Projekt gibt's in der Zeile
oStr = GlobalLock(oBuffer);
den ersten Fehler, irgendwas in der Richtung, dass void nicht in char konvertiert werden kann. Deshalb dachte ich, dass wohl GlobalLock() an der Stelle void zurückliefert - hm. Aber wenn windows.h eingebunden ist, dann müsste GlobalLock() doch funktionieren...? Oder ist das ein Folgefehler, weil strlen() nicht da war und oBuffer deshalb nix gescheites enthält? Wie gesagt: morgen weiß ich mehr.

Zu 4.:
Die DLL soll eben keinen C-artigen, nullterminierten String, sondern einen "BASIC"-artigen String mit Zeichenlängenangabe in den ersten Bytes (max. Länge 32K) zurückgeben. Und eigentlich auch nicht einen Pointer zu einem solchen String sondern direkt den Text eben. Deshalb wundert es mich eigentlich auch, dass diese Funktion funktioniert, wo sie doch einen Pointer auf einen nullterminierten... naja, egal. Haupsache es geht. ;)

Schönen Abend noch,
Martin
 
ManicMarble hat gesagt.:
Die DLL soll eben keinen C-artigen, nullterminierten String, sondern einen "BASIC"-artigen String mit Zeichenlängenangabe in den ersten Bytes (max. Länge 32K) zurückgeben. Und eigentlich auch nicht einen Pointer zu einem solchen String sondern direkt den Text eben. Deshalb wundert es mich eigentlich auch, dass diese Funktion funktioniert, wo sie doch einen Pointer auf einen nullterminierten... naja, egal. Haupsache es geht. ;)
Das haben wir uns ja auch gefragt.
 
Ach ja, zu 3:

Hatte ich vergessen. C++ ist ein bisschen strikter als C, was Pointer-Konvertierung angeht (aber nur ein bisschen, man kann immer noch ganz leicht alles kaputt kriegen). Deshalb muss bei dem GlobalLock ein cast rein:

oStr = (char*)GlobalLock(oBuffer);

[sinnlosgemeckere]
Die C++-Jungens habens zu gut gemeint, und die neuen cast-operatoren dermassen bescheuert (und lang!) benannt, dass ich nie Lust verspüre, die mal tatsächlich zu benutzen. Gut gemacht!
[/sinnlosgemeckere]
 
Guten Morgen.

So, hab's probiert. stdio.h und windows.h sind inkludiert. Aber trotzdem kriege ich den Fehler beim compilieren:
error C2440: '=' : 'void *' kann nicht in 'char *' konvertiert werden
in der Zeile in der das GlobalLock() steht.
(Mist. Und ich war heute früh schon so erwartungsfroh zur Arbeit erschienen... :( )

Wo klemmt's noch?

Martin

PS @ Thomas: Mannomann! 3:31 Uhr. Das nenn' ich mal nen Hardcore-Coder...
 
Hui, da war ich mit meiner Antwort zu schnell. Hatte vor Endurion's Antwort angefangen zu schreiben.

@Endurion: 1000 Dank, das war wohl der Knackpunkt. Jetzt compiliert's zumindest mal. Wird also doch ein guter Tag...
 
Hallo, ich schon wieder.
Compilieren tut's jetzt, aber funktionieren tut's nicht. Hier meine Test-Export-Funktion und nochmal die etwas angepasste "Übergabe"-Funktion:
PHP:
DWORD hallowelt(void) {
	char *txt;
	txt = "Hallo Welt";
	return cStr2oStr(txt);
}

DWORD cStr2oStr(char *cStr) {
	
	HANDLE  oBuffer;
	char   *oStr;
	DWORD   dwOStr = MAKELONG(0, 1);
	size_t  len = 0;
	
	if (cStr) len = strlen(cStr);
	
	oBuffer = GlobalAlloc(GMEM_MOVEABLE, (DWORD)len + 1);
	
	if (oBuffer) {
		oStr = (char*) GlobalLock(oBuffer);
		if (cStr) memcpy(oStr, cStr, len + 1);
		GlobalUnlock(oBuffer);
		dwOStr = MAKELONG(oBuffer, 0);
		//dwOStr = (DWORD) oBuffer;
	}
	
	return (dwOStr);
	
}
In der Ziel-Anwendung (es handelt sich um "Toolbook") sieht der Test dann etwa so aus:
Code:
-- DLL linken (das klappt ohne Fehler):
to handle buttonUp
	clear sysError
	linkDLL32 "StringDLL2.dll"
		STRING hallowelt()
	end linkDll32
	request (sysError = null)
end buttonUp

-- Testen:
to handle buttonUp
	request quote & hallowelt() & quote  -- request gibt eine Messagebox aus
end buttonUp
Beim Testen erhalte ich einen Leerstring oder NULL (ist für Toolbook das selbe).
Wenn ich anstatt dwOStr = MAKELONG(oBuffer, 0); dies hier schreibe:
dwOStr = (DWORD) oBuffer;, dann stürzt Toolbook beim Ausführen von hallowelt() unkontrolliert ab.

Was tun?
 
Zurück