Delphi-DLL in VB 6

Hallo,
Ich hab jetzt meine Test-DLL in Delphi geschrieben. Aus einem Delphi-Programm funktionieren alle Procs, doch in VB führt der Aufruf der Routine

procedure ExchangeData(var A : Integer; var B: Integer); stdcall;

begin
A := 100;
B := 200;
end;

zu einer NT-Schutzverletzung.
In einem VB-Modul hab ich die Funktion so deklariert:

Public Declare Sub ExchangeData Lib „mytest.dll“ (ByVal A As Long, ByVal B As Long)

was stimmt das nicht ?
Die Win32-API hat doch auch viele solcher Procs die Variablen manipulieren…

Hallo

Delphi erstellt doch COM-DLL’s oder?

gruss, Giuseppe

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

brauche aber eine normale DLL.

Hab ByVal durch ByRef ausgetauscht nun geht’s.
Aber mit der Situation nested stringm, also einen String in einem Record hab ichnoch probleme.

Ok,

aber wieso prpgrammierst Du nicht eine COM
DLL? geht doch viel besser :wink:

gruss, Giuseppe

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo,

Delphi kann die Records packen (packed record) oder an Word-Grenzen ausrichten. VB nimmt glaube ich ausgerichtete Records. Bei Delphi haengt es davon ab, ob DU den Record als packed declarierst, und wie die Einstellung in Deinen Optionen ist.

Ansonsten waere es gut, wenn Du die Probleme genauer schildern koenntest!

Niels

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo,

Delphi kann die Records packen (packed
record) oder an Word-Grenzen ausrichten.
VB nimmt glaube ich ausgerichtete
Records. Bei Delphi haengt es davon ab,
ob DU den Record als packed declarierst,
und wie die Einstellung in Deinen
Optionen ist.

also…
in der DLL sind alle Procs ald stdcall deklariert, also C-Standard. Der Record ist ein packed Record.

In Delphi sieht er so aus:

type
TMyStruct = packed record
StrucSize : Cardinal;
clName : Cardinal;
Name : pChar;
Age : Byte;
Flags : Cardinal;
end;

Das gleiche in VB hab ich folgendermassen definiert:

Public Type TMyStruc
StrucSize As Long
clName As Long
Name As String * 500
Age As Byte
Flags As Long
End Type

Eventuell ist es wirklich eine technische Einschränkung von VB, weil’s keine direkten Pointer gibt. Soweit ich weis, gibts beispielsweise auch in der win32-API keine Funktionen die einen Zeiger auf eine Struktur mit einem String darin benutzen.

Die Felder im Record nach dem Aufruf sind:

StrucSize = 25 (stimmt)
clName = 12 (stimmt)
Name = ein paar sonder-zeichen (falsch)
Age und Flags = 0 (falsch)

in Delphi ist die Funktion so deklariert:

function GetStruc(var Buffer : TMyStruct) : Integer; stdcall;

in einem VB-Modul so:

Public Declare Function GetStruc Lib „mytest.dll“ (ByRef Buffer As TMyStruc) As Long

Wenn ich den Stirng direkt als Pointer (bze. ByRef in VB) als Parameter übergebe funktioierts:

Public Declare Function GetString Lib „mytest.dll“ (ByRef clBuffer As Long, ByRef szBuffer As Long, ByRef Buffer As String) As Long

Als Funktionsergebnis geht’s auch nicht.

Public Declare Function ReturnString Lib „mytest.dll“ (ByVal clBuffer As Long, ByRef szBuffer As Long) As String

thx, Roger

also…
in der DLL sind alle Procs ald stdcall
deklariert, also C-Standard. Der Record
ist ein packed Record.

In Delphi sieht er so aus:

type
TMyStruct = packed record
StrucSize : Cardinal;
clName : Cardinal;
Name : pChar;
Age : Byte;
Flags : Cardinal;
end;

Das gleiche in VB hab ich folgendermassen
definiert:

Public Type TMyStruc
StrucSize As Long
clName As Long
Name As String * 500
Age As Byte
Flags As Long
End Type

Da haben wir schon ein Problem:
in VB deklarierst Du Name as String * 500, das heisst 500 Byte gross, in Delphi Name: PChar also mit der Groesse eines Pointers (4 Byte). Da Du in VB nichts mit dem Pointer anfangen kannst, musst Du Delphi umdefinieren => Name: String[500]. Das funktioniert aber auch nicht, da String max. 255 lang sein kann.
Zum anderen darfst Du keinen packed Record verwenden, da VB diese nicht kennt.

Ich schaetze, da wird man nichts machen koennen. Allerdings kenne ich mich mit VB6 nicht optimal aus (ich benutze VB5 und das auch nur selten). Evtl. weiss ja jemand anderes eine Loesung!

Gruss, Niels

Ein dimensionierter String in VB entspricht einem Packed Array of Char in Delphi (also kein PChar, das ist ja ein Pointer)

Reinhard

Kann die Länge eines Strings in VB6 zur Laufzeit geändert werden, wie in Delphi mit SetLength ?

Ansonsten ist ein (packed) array of irgendwas ja auch nur ein Pointer…

Kann die Länge eines Strings in VB6 zur
Laufzeit geändert werden, wie in Delphi
mit SetLength ?

Das ist zwischen dimensionierten und undimensionierten Strings unterschiedlich.

String * 16
sind einfach 16 Byte

String
ist ein String variabler Länge. (Ein Pointer auf eine variable Struktur).

Ansonsten ist ein (packed) array of
irgendwas ja auch nur ein Pointer…

Ein Packed Array ist ein Packed Array und kein Pointer! Ein PChar hingegegen ist ein Pointer.

Reinhard

String * 16
sind einfach 16 Byte

Also, du meinst ein String von bis max. 15 Zeichen (1 für das #0)

String
ist ein String variabler Länge. (Ein
Pointer auf eine variable Struktur).

Wenn ich einer DLL-Proc einen „ungedimmten“ String übergebe kommt ne NT-Access Violation.

Ansonsten ist ein (packed) array of
irgendwas ja auch nur ein Pointer…

Ein Packed Array ist ein Packed Array und
kein Pointer!

Blödsinn.
Belehr mich nicht über Delphi :smile:
Ein Array, ob packed oder nicht, ob fix oder variabel ist in Delphi ein Zeiger auf die Daten

Ein PChar hingegegen ist

ein Pointer.

Ein Delphi-String auch

String * 16
sind einfach 16 Byte

Also, du meinst ein String von bis max.
15 Zeichen (1 für das #0)

Nein - es ist ja kein Zero-Terminated-String (wie in C). Also wirklich 16 Byte - also GENAU 16 Zeichen.

String
ist ein String variabler Länge. (Ein
Pointer auf eine variable Struktur).

Wenn ich einer DLL-Proc einen
„ungedimmten“ String übergebe kommt ne
NT-Access Violation.

Ja, weil du den Pointer nicht dereferenzierst.

Ansonsten ist ein (packed) array of
irgendwas ja auch nur ein Pointer…

Ein Packed Array ist ein Packed Array und
kein Pointer!

Blödsinn.
Belehr mich nicht über Delphi :smile:
Ein Array, ob packed oder nicht, ob fix
oder variabel ist in Delphi ein Zeiger
auf die Daten

Nun ja - jede Variable ist eine Adresse einer Variablen. Dann ist der Pointer halt eine Adresse einer Adresse. Aber wenn du schreibst:

Var C: Packed Array [0…11] of byte

dann werden doch 12 Byte auf dem Stack abgelegt und kein Pointer!

Reinhard

Ein PChar hingegegen ist

ein Pointer.

Ein Delphi-String auch

Nein - aber mir dämmert, was du meinst. Die Parameterübergabe in Delphi erfolgt per Referenz (zumindest bei Var-Parametern). Insofern wird als Parameter die Adresse des Strings übergeben. (Das ist in VB übrigens genauso, es sei denn, du wählst explizit ByVal als Übergabemodus.)
Wenn du aber einen PChar übergibst, wird die Adresse des Pointers auf ein Array of Char übergeben, wenn du ein Array of Char übergibst, wird dessen Adresse übergeben. Schau dir mal die Definition von PChar an, da steht ein kleines ^ davor!

Also: ein Array ist wirklich kein Pointer -
ein PChar ist ein Pointer.

VB übernimmt übrigens die Dereferenzierung und das NUL-Padding bei String-(Nicht String *) Variablen (Wenn man ByVal davor setzt!).

Reinhard

PS: Da fällt mir die Anekdote von Niklaus Wirth ein. Gefragt, wie sich denn sein Nachname ausspreche, sagte er: „If you call me by name, it’s ‚wirt‘, if you call me by value, it’s ‚worth‘…“

Ein PChar hingegegen ist

ein Pointer.

Ein Delphi-String auch

Nein(…)

Wir könnten jetzt wetten wer recht hat :smile:
Von der Logik her betrachtet würde ich eher sagen auf dem, doch recht begrenzten Stack, würde nur ein Idiot Daten ablegen, eher ein Pointer auf irgendwas irgendwo im Speicher,
ist viel effizienter, du willst ja kein Stack Overflow…

  • aber mir dämmert, was du meinst.

Die Parameterübergabe in Delphi
erfolgt per Referenz (zumindest bei
Var-Parametern).

Exakt, sonst entspricht es ByVal

Insofern wird als
Parameter die Adresse des Strings
übergeben. (Das ist in VB übrigens
genauso, es sei denn, du wählst explizit
ByVal als Übergabemodus.)

Da wird’s jetzt heikel…
Kann sein, dass das bei „normalen“ Strings, also solchen mit nur einem Längen-Byte die bis zu 255 Zeichen lang sein können und fixen Strings, so ist.
Die neuen huge Strings, mit 4 Byte-Integer längen-Deskriptor sind aber Pointer auf das erste Zeichen des Strings. Der Length-Descriptor hat also einen negativen Offset (-4) da diese 4 Byte davor stehen. Die SetLength-Proc ändert daher diese Length-Deskriptor und alloziert Speicher, bzw. gibt überschüssigen Speicher frei, wenn nötig. Deshalb musst du mit Pointer(SomeStr)^, arbeiten wenn du den Inhalt direkt ansprechen willst, z.B. um ihn in eine binäre Datei zu schreiben.

Wenn du aber einen PChar übergibst, wird
die Adresse des Pointers auf ein Array of
Char übergeben, wenn du ein Array of Char
übergibst, wird dessen Adresse übergeben.

Trotzdem haben wir’s mit nem PTR zu tun :smile:

VB übernimmt übrigens die
Dereferenzierung und das NUL-Padding bei
String-(Nicht String *) Variablen (Wenn
man ByVal davor setzt!).

hmh…habe gelesen, dass VB intern eine Kopie des Strings erstellt und zwar im Format von C, also null-term und der DLL-Funktion einen Pointer auf diese Kopie übergibt, danach wird er wieder zurück konventiert und der VB-Coder kann normal damit arbeiten. Deshalb muss dieser gross genug sein.

Reinhard

PS: Da fällt mir die Anekdote von Niklaus
Wirth ein. Gefragt, wie sich denn sein
Nachname ausspreche, sagte er: „If you
call me by name, it’s ‚wirt‘, if you call
me by value, it’s ‚worth‘…“

)

Wir könnten jetzt wetten wer recht hat :smile:
Von der Logik her betrachtet würde ich
eher sagen auf dem, doch recht begrenzten
Stack, würde nur ein Idiot Daten ablegen,
eher ein Pointer auf irgendwas irgendwo
im Speicher,
ist viel effizienter, du willst ja kein
Stack Overflow…

Prozedurparameter landen nun einmal auf dem Stack, ebenso die lokalen Parameter einer Prozedur. (Wir reden jetzt von Arrays of Char, nicht von Huge Strings o.ä.)

Also, noch einmal: PChar ist nun einmal ein Pointer:

Var P: PChar; A: Array[0…11] of Char

Auf die Elemente von P beziehe ich mich, indem ich P dereferenziere:

P^[2] := ‚a‘

auf die Elemente von A kann ich mich direkt beziehen, da gibt es nichts zu dereferenzieren:

A[2] := ‚a‘

Ein Array of Char ist definitiv kein Pointer auf ein Array of Char - davon beisst die Maus doch keinen Faden ab!

Wenn du aber einen PChar übergibst, wird
die Adresse des Pointers auf ein Array of
Char übergeben, wenn du ein Array of Char
übergibst, wird dessen Adresse übergeben.

Trotzdem haben wir’s mit nem PTR zu tun

NEIN NEIN NEIN!

Wenn ich schreibe:
Type CharArr: Array [0…9] of Char;
Var P: PChar; A: CharArr;

dann wird bei

Procedure A(Var P: PChar);

die Adresse des Pointers auf den Stack gelegt (also eine zweifache Referenz!). Bei

Procedure B(P: PChar);

kommt der Pointer auf den Stack, bei

Procedure C(Var A: CharArr);

wird die Adresse des Arrays auf den Stack gelegt (das könnte man als Pointer bezeichnen - ist es aber im Sinne von Pascal bzw. Delphi nicht) und bei

Procedure D(A: CharArr);

landet das gesamte Array auf dem Stack!

M.W. sollte das jeweils den folgenden VB-Aufrufen entsprechen:

Sub A(ByRef S as String) ’ mit Einschränkungen!!!
Sub B(ByVal S As String)
Sub C(ByRef S As String*10)
Sub D(ByVal S As String*10)

(Fall A dürfte nicht funktionieren, da die Art der Verpointerung in VB eine andere ist und VB-Strings noch ein Wort am Anfang als Längenzähler mitführen).

Reinhard

Also, noch einmal: PChar ist nun einmal
ein Pointer:

Ja ich weis es ja :stuck_out_tongue:

Var P: PChar; A: Array[0…11] of Char

Auf die Elemente von P beziehe ich mich,
indem ich P dereferenziere:

P^[2] := ‚a‘

auf die Elemente von A kann ich mich
direkt beziehen, da gibt es nichts zu
dereferenzieren:

A[2] := ‚a‘

Ein Array of Char ist definitiv kein
Pointer auf ein Array of Char - davon
beisst die Maus doch keinen Faden ab!

Ich glaube es ist ein interner Pointer, der Delphi erwaltet. Aähnliche der Situation in VB mit ByRef. Du hast’s mir Pointer zu tun, ohne das du’s (direkt) merkst

Trotzdem haben wir’s mit nem PTR zu tun

NEIN NEIN NEIN!

Herr Gott…

Wenn ich schreibe:
Type CharArr: Array [0…9] of Char;
Var P: PChar; A: CharArr;

dann wird bei

Procedure A(Var P: PChar);

die Adresse des Pointers auf den Stack
gelegt (also eine zweifache Referenz!).

Dachte die Parameter werden direkt in den Prozessor-Register übergeben aus Geschwindigkeitsgründen. Ausser man folgt dem C-Standard in dem das Keyword STDCALL hiner die Proc gesetzt wird, dann gehen die Argument auf den Stack.

Trotzdem hast du für einen VB-Programmierer ziemlich Ahnung :smile:
Die meisten wissen das ned :stuck_out_tongue:

Dachte die Parameter werden direkt in den
Prozessor-Register übergeben aus
Geschwindigkeitsgründen. Ausser man folgt
dem C-Standard in dem das Keyword STDCALL
hiner die Proc gesetzt wird, dann gehen
die Argument auf den Stack.

Ich denke, wir reden hier über eine DLL? Eine DLL sollte die üblichen DLL-Aufrufkonventionen verwenden, wenn VB etwas mit der DLL anfangen soll, sollte die Aufrufkonvention also STDCALL sein! (C-Standard ist übrigens CDECL)

Reinhard

Können auch in VB DLLs erstellt werden ?
stimmt.
Können auch in VB DLLs erstellt werden, die wiederum in Delphi genutzt werden können ?

stimmt.
Können auch in VB DLLs erstellt werden,
die wiederum in Delphi genutzt werden
können ?

Normale DLLs nicht, nur ActiveX-DLLs, siehe z.B.:
http://support.microsoft.com/support/kb/articles/Q17…

Reinhard