inversojvo
asked on
Calling dll from VBA and getting a big complex UDT as a return value - "bad calling convention" error.
I've a C dll (from the third company - so I can't change and recompile it) accessing a GIS-System database. This is not a COM dll - so the functions can be accessed from VBA only with "Declare". This dll has 6 functions, that have to be called in a logical order to get my query to GIS db "answered".
From the C header file the parts that seem to be relevant (I'm not a C programmer - so I may make a mistake), look as following:
1) UDT structures:
typedef struct SVersionsInfo
{
char Version[6];
char Zusatz[6];
TDatum DatTabellen;
char Beschreibung[256];
} TVersionsInfo;
// Attribute der Entitдt "GisRisiko":
typedef struct SGisRisiko
{
char Sturm[2];
char Erdsenkung[2];
char Ueberschwemmung[2];
} TGisRisiko;
// Attribute der Entitдt "GisAbfrageParameter":
typedef struct SGisAbfrageParameter
{
TBool befuelleTabellen;
TBool KzRisikoKlassifizierung;
TBool exakteSuche;
char PLZ[7];
char ID_PLZ[8];
char Ort[51];
char ID_Ort[8];
char Strasse[51];
char ID_Strasse[8];
char Hausnummer[51];
char ID_Hausnummer[8];
} TGisAbfrageParameter;
// Attribute der Entitдt "TGisDatenSet":
typedef struct SGisDatenSet
{
TBool befuellt;
//TBool gefunden;
TBool eindeutig;
short Status_PLZ;
char ID_PLZ[8];
short Status_Ort;
char ID_Ort[8];
short Status_Strasse;
char ID_Strasse[8];
short Status_Hausnummer;
char ID_Hausnummer[8];
TAlphanumListbox LbPLZ;
TAlphanumListbox LbOrt;
TAlphanumListbox LbStrasse;
TAlphanumListbox LbHausnummer;
TGisRisiko RisikoKlassifizierung;
} TGisDatenSet;
// Attribute der Entitдt "TGisAbfrage":
typedef struct SGisAbfrage
{
TMeldung *Meldung;
TVersionsInfo *VersionsInfo;
TGisAbfrageParameter *GisAbfrageParameter;
TGisDatenSet *GisDatenSet;
} TGisAbfrage;
// Boolean-Typ
typedef short TBool; // 19.01.98, S.J.
#ifndef SWIG
#define false 0 // 19.01.98, S.J.
#define true 1 // 19.01.98, S.J.
#endif
// Datum-Typ
typedef struct SDatum
{
short Jahr;
unsigned short Monat;
unsigned short Tag;
} TDatum;
// Wertepaar von zwei Zeichenketten
typedef struct SAlphanumWertepaar
{
char Schluessel[51]; // 18.12.97, S.J.
char Wert[51];
} TAlphanumWertepaar;
// Listbox mit alphanumerischem Schlьssel
typedef struct SAlphanumListbox
{
short Anzahl;
TAlphanumWertepaar *Werte;
char AktuellerSchluessel[51]; // 18.12.97, S.J.
} TAlphanumListbox;
// Definition der Ьbergabestruktur fьr Meldungen der DLLs
typedef struct SMeldung
{
char Typ; // 18.12.97, S.J.
short Nummer; // 18.12.97, S.J.
char Text[256]; // 15.06.98, S.J.
char Hilfe[51]; // 15.06.98, S.J.
} TMeldung;
2) exported functions (in the order of a usual call):
//Exportierte Funktionen
//
short API_EXPORT initGisAbfrage(char* PfadDLL, char *PfadDaten);
TGisAbfrage* API_EXPORT erzeugeAbfrageStruktur(voi d);
TBool API_EXPORT ausfuehrenAbfrage(TGisAbfr age*);
TBool API_EXPORT loescheAbfrage(TGisAbfrage *);
TBool API_EXPORT loescheAbfrageStruktur(TGi sAbfrage*) ;
TBool API_EXPORT exitGisAbfrage(void);
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ------
In VBA the test module has the following code:
Public Type Bool
Bool As Integer
End Type
Public Type Datum
Jahr As Integer
Monat As Integer
Tag As Integer
End Type
Public Type AlphanumWertepaar
Schluessel As String * 51
Wert As String * 51
End Type
Public Type AlphanumListbox
Anzahl As Integer
Werte As AlphanumWertepaar
AktuellerScluessel As String * 51
End Type
Public Type VersionsInfo
Version As String * 6
Zusatz As String * 6
DatTabellen As Datum
Beschreibung As String * 256
End Type
Public Type GisRisiko
Sturm As String * 2
Erdsenkung As String * 2
Ueberschwemmung As String * 2
End Type
Public Type GisAbfrageParameter
befuelleTabellen As Bool
KzRisikoKlassifizierung As Bool
exakteSuche As Bool
PLZ As String * 7
ID_PLZ As String * 8
Ort As String * 51
ID_Ort As String * 8
Strasse As String * 51
ID_Strasse As String * 8
Hausnummer As String * 51
ID_Hausnummer As String * 8
End Type
Public Type GisDatenSet
befuellt As Bool
eindeutig As Bool
Status_PLZ As Integer
ID_PLZ As String * 8
Status_Ort As Integer
ID_Ort As String * 8
Status_Strasse As Integer
ID_Strasse As String * 8
Status_Hausnummer As Integer
ID_Hausnummer As String * 8
LbPLZ As AlphanumListbox
LbOrt As AlphanumListbox
LbStrasse As AlphanumListbox
LbHausnummer As AlphanumListbox
RisikoKlassifizierung As GisRisiko
End Type
Public Type Meldung
Typ As Byte
Nummer As Integer
Text As String * 256
Hilfe As String * 51
End Type
Public Type GisAbfrage
Meldung As Meldung
VersionsInfo As VersionsInfo
GisAbfrageParameter As GisAbfrageParameter
GisDatenSet As GisDatenSet
End Type
Const PfadDll As String = "d:\Gis"
Const PfadDaten As String = "d:\Gis\Daten"
Declare Function initGisAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByVal str1 As String, ByVal str2 As String) As Integer
Declare Function erzeugeAbfrageStruktur Lib "D:\Gis\GisAbfrage32.dll" () As GisAbfrage
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Declare Function loescheAbfrage Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Declare Function loescheAbfrageStruktur Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Declare Function exitGisAbfrage Lib "D:\Gis\GisAbfrage32.dll" () As Boolean
Sub test()
Dim intResult As Integer
Dim GAbfrage As GisAbfrage
Dim blnTmp As Boolean
Dim intTmp As Integer
Dim lngTmp As Long
Const sProcSig As String = MODULE_NAME & "test"
On Error GoTo PROC_ERR
intResult = RESULT_OK
intTmp = initGisAbfrage(PfadDll, PfadDaten)
GAbfrage = erzeugeAbfrageStruktur()
blnTmp = ausfuerenAbfrage(GAbfrage)
blnTmp = loescheAbfrage(GAbfrage)
blnTmp = loescheAbfrageStruktur(GAb frage)
blnTmp = exitGisAbfrage()
PROC_EXIT:
Exit Sub
PROC_ERR:
intResult = RESULT_ERROR
If errTypeReport(sProcSig) <> Critical Then Resume Next
Resume PROC_EXIT
'Resume
End Sub
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
I get an error "bad calling convention", Nr. 49 for this line:
GAbfrage = erzeugeAbfrageStruktur()
and think, that the UDT, that is to be created with erzeugeAbfrageStruktur() in C dll differs from the UDT I've tried to mirror in VBA module. The UDT GAbfrage has a complex structure (appr. 60 primitive variables, differently grouped). How can I make the test function run?
Some colleague of mine had made this dll work for Java programm, but for mapping the UDT from C to Java they used "SWIG" tool (http://www.swig.org). I haven't found smth similar to map the structure to VB-VBA.
I assume that the order and the variables type in my UDT are the keys, but how to check the conformity of allocated memory in C and in VBA? Are there some special tools? As the UDT is big and complex, I can't test it part by part (or just don't know, how to test it by parts :( ). The function runs or doesn't run and to have it run, I have to have the whole UDT correct.
So, please help.
From the C header file the parts that seem to be relevant (I'm not a C programmer - so I may make a mistake), look as following:
1) UDT structures:
typedef struct SVersionsInfo
{
char Version[6];
char Zusatz[6];
TDatum DatTabellen;
char Beschreibung[256];
} TVersionsInfo;
// Attribute der Entitдt "GisRisiko":
typedef struct SGisRisiko
{
char Sturm[2];
char Erdsenkung[2];
char Ueberschwemmung[2];
} TGisRisiko;
// Attribute der Entitдt "GisAbfrageParameter":
typedef struct SGisAbfrageParameter
{
TBool befuelleTabellen;
TBool KzRisikoKlassifizierung;
TBool exakteSuche;
char PLZ[7];
char ID_PLZ[8];
char Ort[51];
char ID_Ort[8];
char Strasse[51];
char ID_Strasse[8];
char Hausnummer[51];
char ID_Hausnummer[8];
} TGisAbfrageParameter;
// Attribute der Entitдt "TGisDatenSet":
typedef struct SGisDatenSet
{
TBool befuellt;
//TBool gefunden;
TBool eindeutig;
short Status_PLZ;
char ID_PLZ[8];
short Status_Ort;
char ID_Ort[8];
short Status_Strasse;
char ID_Strasse[8];
short Status_Hausnummer;
char ID_Hausnummer[8];
TAlphanumListbox LbPLZ;
TAlphanumListbox LbOrt;
TAlphanumListbox LbStrasse;
TAlphanumListbox LbHausnummer;
TGisRisiko RisikoKlassifizierung;
} TGisDatenSet;
// Attribute der Entitдt "TGisAbfrage":
typedef struct SGisAbfrage
{
TMeldung *Meldung;
TVersionsInfo *VersionsInfo;
TGisAbfrageParameter *GisAbfrageParameter;
TGisDatenSet *GisDatenSet;
} TGisAbfrage;
// Boolean-Typ
typedef short TBool; // 19.01.98, S.J.
#ifndef SWIG
#define false 0 // 19.01.98, S.J.
#define true 1 // 19.01.98, S.J.
#endif
// Datum-Typ
typedef struct SDatum
{
short Jahr;
unsigned short Monat;
unsigned short Tag;
} TDatum;
// Wertepaar von zwei Zeichenketten
typedef struct SAlphanumWertepaar
{
char Schluessel[51]; // 18.12.97, S.J.
char Wert[51];
} TAlphanumWertepaar;
// Listbox mit alphanumerischem Schlьssel
typedef struct SAlphanumListbox
{
short Anzahl;
TAlphanumWertepaar *Werte;
char AktuellerSchluessel[51]; // 18.12.97, S.J.
} TAlphanumListbox;
// Definition der Ьbergabestruktur fьr Meldungen der DLLs
typedef struct SMeldung
{
char Typ; // 18.12.97, S.J.
short Nummer; // 18.12.97, S.J.
char Text[256]; // 15.06.98, S.J.
char Hilfe[51]; // 15.06.98, S.J.
} TMeldung;
2) exported functions (in the order of a usual call):
//Exportierte Funktionen
//
short API_EXPORT initGisAbfrage(char* PfadDLL, char *PfadDaten);
TGisAbfrage* API_EXPORT erzeugeAbfrageStruktur(voi
TBool API_EXPORT ausfuehrenAbfrage(TGisAbfr
TBool API_EXPORT loescheAbfrage(TGisAbfrage
TBool API_EXPORT loescheAbfrageStruktur(TGi
TBool API_EXPORT exitGisAbfrage(void);
--------------------------
In VBA the test module has the following code:
Public Type Bool
Bool As Integer
End Type
Public Type Datum
Jahr As Integer
Monat As Integer
Tag As Integer
End Type
Public Type AlphanumWertepaar
Schluessel As String * 51
Wert As String * 51
End Type
Public Type AlphanumListbox
Anzahl As Integer
Werte As AlphanumWertepaar
AktuellerScluessel As String * 51
End Type
Public Type VersionsInfo
Version As String * 6
Zusatz As String * 6
DatTabellen As Datum
Beschreibung As String * 256
End Type
Public Type GisRisiko
Sturm As String * 2
Erdsenkung As String * 2
Ueberschwemmung As String * 2
End Type
Public Type GisAbfrageParameter
befuelleTabellen As Bool
KzRisikoKlassifizierung As Bool
exakteSuche As Bool
PLZ As String * 7
ID_PLZ As String * 8
Ort As String * 51
ID_Ort As String * 8
Strasse As String * 51
ID_Strasse As String * 8
Hausnummer As String * 51
ID_Hausnummer As String * 8
End Type
Public Type GisDatenSet
befuellt As Bool
eindeutig As Bool
Status_PLZ As Integer
ID_PLZ As String * 8
Status_Ort As Integer
ID_Ort As String * 8
Status_Strasse As Integer
ID_Strasse As String * 8
Status_Hausnummer As Integer
ID_Hausnummer As String * 8
LbPLZ As AlphanumListbox
LbOrt As AlphanumListbox
LbStrasse As AlphanumListbox
LbHausnummer As AlphanumListbox
RisikoKlassifizierung As GisRisiko
End Type
Public Type Meldung
Typ As Byte
Nummer As Integer
Text As String * 256
Hilfe As String * 51
End Type
Public Type GisAbfrage
Meldung As Meldung
VersionsInfo As VersionsInfo
GisAbfrageParameter As GisAbfrageParameter
GisDatenSet As GisDatenSet
End Type
Const PfadDll As String = "d:\Gis"
Const PfadDaten As String = "d:\Gis\Daten"
Declare Function initGisAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByVal str1 As String, ByVal str2 As String) As Integer
Declare Function erzeugeAbfrageStruktur Lib "D:\Gis\GisAbfrage32.dll" () As GisAbfrage
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Declare Function loescheAbfrage Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Declare Function loescheAbfrageStruktur Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Declare Function exitGisAbfrage Lib "D:\Gis\GisAbfrage32.dll" () As Boolean
Sub test()
Dim intResult As Integer
Dim GAbfrage As GisAbfrage
Dim blnTmp As Boolean
Dim intTmp As Integer
Dim lngTmp As Long
Const sProcSig As String = MODULE_NAME & "test"
On Error GoTo PROC_ERR
intResult = RESULT_OK
intTmp = initGisAbfrage(PfadDll, PfadDaten)
GAbfrage = erzeugeAbfrageStruktur()
blnTmp = ausfuerenAbfrage(GAbfrage)
blnTmp = loescheAbfrage(GAbfrage)
blnTmp = loescheAbfrageStruktur(GAb
blnTmp = exitGisAbfrage()
PROC_EXIT:
Exit Sub
PROC_ERR:
intResult = RESULT_ERROR
If errTypeReport(sProcSig) <> Critical Then Resume Next
Resume PROC_EXIT
'Resume
End Sub
--------------------------
I get an error "bad calling convention", Nr. 49 for this line:
GAbfrage = erzeugeAbfrageStruktur()
and think, that the UDT, that is to be created with erzeugeAbfrageStruktur() in C dll differs from the UDT I've tried to mirror in VBA module. The UDT GAbfrage has a complex structure (appr. 60 primitive variables, differently grouped). How can I make the test function run?
Some colleague of mine had made this dll work for Java programm, but for mapping the UDT from C to Java they used "SWIG" tool (http://www.swig.org). I haven't found smth similar to map the structure to VB-VBA.
I assume that the order and the variables type in my UDT are the keys, but how to check the conformity of allocated memory in C and in VBA? Are there some special tools? As the UDT is big and complex, I can't test it part by part (or just don't know, how to test it by parts :( ). The function runs or doesn't run and to have it run, I have to have the whole UDT correct.
So, please help.
Can you declare erzeugeAbfrageStruktur to return a Long (so that you get a pointer to the data)? Or to return a Variant? Or possibly even "As Any"? It's hard to follow what the original structure looks like to see if its possible to mimic with a VB UDT directly.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Hi inversojvo,
If you can't recompile the DLL, you're probably out of luck... unless the functions were exported with the pascal calling convention then they will be popped off the stack in the opposite order that VB pushed them onto the stack... thus the bad calling convention... about the only other thing you could do is create a "wrapper" dll which would have functions which are exported with the pascal calling convention, and those functions in turn call the original dll, get the results back and relay it back to your application.
Cheers!
If you can't recompile the DLL, you're probably out of luck... unless the functions were exported with the pascal calling convention then they will be popped off the stack in the opposite order that VB pushed them onto the stack... thus the bad calling convention... about the only other thing you could do is create a "wrapper" dll which would have functions which are exported with the pascal calling convention, and those functions in turn call the original dll, get the results back and relay it back to your application.
Cheers!
ASKER
Unfortunately, can't prove the ideas immediately - code will be available again only on Monday :(.
2 AzraSound : I'll try with long and Variant and will report results. I've already tried with Any and got an error (compillation error, as far as I remember).
2 EDDYKT : does such type exist? I've just tried in VBA under Access and also got "Compillation Error" - "End of expression expected" (all error messages are translated from German)
2 mdougan : yeas, you've right (I read about it just today), that there are 2 conventions (and that one of them can cause this error 49). But I looked throught the C header file and , and noticed smth. with _std.., not with _cdecl, so this aspect seems to be OK.
2 AzraSound : I'll try with long and Variant and will report results. I've already tried with Any and got an error (compillation error, as far as I remember).
2 EDDYKT : does such type exist? I've just tried in VBA under Access and also got "Compillation Error" - "End of expression expected" (all error messages are translated from German)
2 mdougan : yeas, you've right (I read about it just today), that there are 2 conventions (and that one of them can cause this error 49). But I looked throught the C header file and , and noticed smth. with _std.., not with _cdecl, so this aspect seems to be OK.
Well, if you've been able to call any of the function successfully, then I'd have to agree with you. Calling C Dlls is further complicated by the fact that String handling in VB is dynamic, whereas C is expecting Fixed Length strings, so, often, you have to be careful declaring string variables to send to the C program, and you have to use the CopyMemory API to copy any strings that were created in the C DLL and passed back (unless you passed a string buffer by reference which the DLL filled). The best source of samples for calling complex C DLL routimes with complex structures is Karl Peterson's website...
I haven't been there for .NET code yet, but you might gain a lot from reading though one of his samples...
http://www.mvps.org/vb/
download the sample NetUser.zip or NetWksta.zip ... even if you don't have VB 6 installed, at least you can edit the class files in notepad or something and see what he does.
I haven't been there for .NET code yet, but you might gain a lot from reading though one of his samples...
http://www.mvps.org/vb/
download the sample NetUser.zip or NetWksta.zip ... even if you don't have VB 6 installed, at least you can edit the class files in notepad or something and see what he does.
ASKER
So, I've tried with "long" as a return type and at least I didn't get an error - seems to be the right direction. Suppose, that now I get the "pointer" to the structure as long:
lngTmp = erzeugeAbfrageStruktur()
Resuming the state, in test functions:
intTmp = initGisAbfrage(PfadDll, PfadDaten)
GAbfrage = erzeugeAbfrageStruktur()
blnTmp = ausfuerenAbfrage(GAbfrage)
blnTmp = loescheAbfrage(GAbfrage)
blnTmp = loescheAbfrageStruktur(GAb frage)
blnTmp = exitGisAbfrage()
2 first functions run without error and now I get the error 453 (dll entry point is not found) already in 3-rd function:
blnTmp = ausfuerenAbfrage(GAbfrage)
I remind, that in C header the correspondent fucntion looks so:
TBool API_EXPORT ausfuehrenAbfrage(TGisAbfr age*);
I made 2 corrections, trying to debug the error:
1) changed the type of return value to Integer, as TBool in C is of type "short" in fact
2) changed the type of parameter in declaration and in calling:
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByVal lng As Long) As Integer
...
Sub test()
...
intTmp = ausfuerenAbfrage(ByVal lngTmp)
...
End Sub
but without success :( - the same error 453.
Might it be, that the error this time is caused by the fact, that the structure, pointed with this lngTmp, is not filled correctly with my values (it's not filled at all at this stage, in fact)? The pointed structure is divided (I think) according to C-types, with all these "unsigned short" and TBool that-is-Integer-in-fact, and so on. But it has to say smth about wrong arguments or again about "bad calling conventions", not that the "entry point is not found".
What do you think?
lngTmp = erzeugeAbfrageStruktur()
Resuming the state, in test functions:
intTmp = initGisAbfrage(PfadDll, PfadDaten)
GAbfrage = erzeugeAbfrageStruktur()
blnTmp = ausfuerenAbfrage(GAbfrage)
blnTmp = loescheAbfrage(GAbfrage)
blnTmp = loescheAbfrageStruktur(GAb
blnTmp = exitGisAbfrage()
2 first functions run without error and now I get the error 453 (dll entry point is not found) already in 3-rd function:
blnTmp = ausfuerenAbfrage(GAbfrage)
I remind, that in C header the correspondent fucntion looks so:
TBool API_EXPORT ausfuehrenAbfrage(TGisAbfr
I made 2 corrections, trying to debug the error:
1) changed the type of return value to Integer, as TBool in C is of type "short" in fact
2) changed the type of parameter in declaration and in calling:
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByVal lng As Long) As Integer
...
Sub test()
...
intTmp = ausfuerenAbfrage(ByVal lngTmp)
...
End Sub
but without success :( - the same error 453.
Might it be, that the error this time is caused by the fact, that the structure, pointed with this lngTmp, is not filled correctly with my values (it's not filled at all at this stage, in fact)? The pointed structure is divided (I think) according to C-types, with all these "unsigned short" and TBool that-is-Integer-in-fact, and so on. But it has to say smth about wrong arguments or again about "bad calling conventions", not that the "entry point is not found".
What do you think?
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
OK, I did it, but the error is the same - 453, when I try to run
blnTmp = ausfuerenAbfrage(GAbfrage)
I'm not sure, that the structure, pointed by lngPointer (created by dll function), fits exactly to the structure of my GAbfrage. How can I see the dll structure (or, at least, the length of this structure)?
blnTmp = ausfuerenAbfrage(GAbfrage)
I'm not sure, that the structure, pointed by lngPointer (created by dll function), fits exactly to the structure of my GAbfrage. How can I see the dll structure (or, at least, the length of this structure)?
After the CopyMemory call, set a breakpoint and look at the contents of the GAbfrage structure and see if it looks correct or not.
ASKER
In fact, the question :
"How can I recognize the structure of UDT, got from C dll, if I get the pointer to this structure"
seems to be big enough to make it a separate question, worth, f.ex. 400 points, doesn't it ? :)
"How can I recognize the structure of UDT, got from C dll, if I get the pointer to this structure"
seems to be big enough to make it a separate question, worth, f.ex. 400 points, doesn't it ? :)
ASKER
I don't know, what has to be there :(.
The initialized structure has to be an empty structure, in fact. Then I have to fill some parts of it with my values, and these values have to play a role of parameters ("Abfrage" means "Query" in German). And then I have to treat the values of some other structure's fields as a result (or results, if there are some).
The initialized structure has to be an empty structure, in fact. Then I have to fill some parts of it with my values, and these values have to play a role of parameters ("Abfrage" means "Query" in German). And then I have to treat the values of some other structure's fields as a result (or results, if there are some).
Error 453 means your function declaration is incorrect? Which function is it that raises the error?
ASKER
damn!! How stupid am I!
Sorry, AzraSound, you pointed right - the name was written wrong, in fact. So, I corrected it and now I'm trying to make the function ausfuerenAbfrage() run.
Here are all relevant declarations for this functon:
Public Type Bool ' type Bool corresponds to C declaration: typedef short TBool;
Bool As Integer
End Type
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByVal lng As Long) As Bool
' corresponds to C declaration: TBool API_EXPORT ausfuehrenAbfrage(TGisAbfr age*);
And the test function:
Sub test()
Dim GAbfrage As GisAbfrage
Dim b As Bool
Dim lngTmp As Long
intTmp = initGisAbfrage(PfadDll, PfadDaten)
lngTmp = erzeugeAbfrageStruktur()
CopyMemory ByVal GAbfrage, lngTmp, LenB(GAbfrage)
b = ausfuehrenAbfrage(ByVal lngTmp)
...
End Sub
So, while trying to execute the expression: b = ausfuerenAbfrage(ByVal lngTmp)
I get the crash Access error :
"The expression in "0x00000000" refers to memory address in "0x00000000". A try to "read" can't be executed for the memory. Click OK - to close the programm, Click Cancel - to debug"
When I click OK, the Access is shut down.
So, I think, I give the wrong structured (and wrong sized) argument to the function ausfuehrenAbfrage().
I've just opened as the extra question "how to get in VBA the UDT structure, if I have only the pointer":
https://www.experts-exchange.com/questions/21163658/How-can-I-recognize-parse-the-structure-of-UDT-returned-from-C-dll-if-I-get-the-pointer-to-this-structure-in-a-VBA-function.html
Sorry, AzraSound, you pointed right - the name was written wrong, in fact. So, I corrected it and now I'm trying to make the function ausfuerenAbfrage() run.
Here are all relevant declarations for this functon:
Public Type Bool ' type Bool corresponds to C declaration: typedef short TBool;
Bool As Integer
End Type
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByVal lng As Long) As Bool
' corresponds to C declaration: TBool API_EXPORT ausfuehrenAbfrage(TGisAbfr
And the test function:
Sub test()
Dim GAbfrage As GisAbfrage
Dim b As Bool
Dim lngTmp As Long
intTmp = initGisAbfrage(PfadDll, PfadDaten)
lngTmp = erzeugeAbfrageStruktur()
CopyMemory ByVal GAbfrage, lngTmp, LenB(GAbfrage)
b = ausfuehrenAbfrage(ByVal lngTmp)
...
End Sub
So, while trying to execute the expression: b = ausfuerenAbfrage(ByVal lngTmp)
I get the crash Access error :
"The expression in "0x00000000" refers to memory address in "0x00000000". A try to "read" can't be executed for the memory. Click OK - to close the programm, Click Cancel - to debug"
When I click OK, the Access is shut down.
So, I think, I give the wrong structured (and wrong sized) argument to the function ausfuehrenAbfrage().
I've just opened as the extra question "how to get in VBA the UDT structure, if I have only the pointer":
https://www.experts-exchange.com/questions/21163658/How-can-I-recognize-parse-the-structure-of-UDT-returned-from-C-dll-if-I-get-the-pointer-to-this-structure-in-a-VBA-function.html
What about the initial declaration?
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Can you call:
b = ausfuerenAbfrage(GAbfrage)
Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean
Can you call:
b = ausfuerenAbfrage(GAbfrage)
ASKER
No, the same error <.. memory "0x00000000" ...> and Access crashes. :(.
Don't you think, that the unproper structure could cause it?
Don't you think, that the unproper structure could cause it?
It does sound like it. Part of its is the char datatypes that you are turning into fixed length strings. Strings in VB are unicode, two bytes, versus one byte that a char is. EDDYKT's initial suggestion was along the right path, just the syntax was a bit off. E.g., instead of:
s As String * 10
you would use
s(1 to 10) As Byte
However these arrays would introduce some issues with our CopyMemory idea, but if we can get the structure into a workable form, you may be able to use the structure directly as you originally intended to do.
s As String * 10
you would use
s(1 to 10) As Byte
However these arrays would introduce some issues with our CopyMemory idea, but if we can get the structure into a workable form, you may be able to use the structure directly as you originally intended to do.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
OK. Then enough for today, and tomorrow in the morning I'll try the following:
I'll list the whole structure somewhere in a column of Excel sheet (all elements, a line for each elementar type), write in next column the sizes in C, in next one - the sizes in VB. If any of size can't be mapped direct, I'll create a small UDT made from Byte array of proper size to map it. Smth like:
Type uStr10
uVal(1 to 10) as Byte
End Type
And after that I'll try CopyMemory again.
Could you please, confirm the following correspondences btw. C and VBA?:
char [NN] --> uStrNN
short --> int
unsigned short --> int (?? The value might have to be read differently, but the size anyway has to be OK, hasn't it?)
There is another thing I'm afraid of - the DWORD alignment (4 Bytes alignment) in structures.. But we'll think of it, if all abovementioned will fail.
I'll list the whole structure somewhere in a column of Excel sheet (all elements, a line for each elementar type), write in next column the sizes in C, in next one - the sizes in VB. If any of size can't be mapped direct, I'll create a small UDT made from Byte array of proper size to map it. Smth like:
Type uStr10
uVal(1 to 10) as Byte
End Type
And after that I'll try CopyMemory again.
Could you please, confirm the following correspondences btw. C and VBA?:
char [NN] --> uStrNN
short --> int
unsigned short --> int (?? The value might have to be read differently, but the size anyway has to be OK, hasn't it?)
There is another thing I'm afraid of - the DWORD alignment (4 Bytes alignment) in structures.. But we'll think of it, if all abovementioned will fail.
ASKER
2 Mike - while I was writing my answer, you posted smth interesting. I'm now away for some hours, then will read your advice attentively - it sounds rather promissing, what you've written!
ASKER
2 Mike:
Yes, you are right about long pointer instead of UDT. To check it I made a dummy dump array (1 to 2000) of bytes and copied there first 2000 bytes beginning from the pointed address. And I got only 8 first bytes filled with values > 0. The following were just zeros.
So, it's clear for me now that I shoud fill all members with its own CopyMemory. But what have I do, if the pointer is somewhere deeper in the structure? I'll explain what I mean:
In GisAbfrage I have a pointer to the member GisDatenSet, that has in its own structure another member of type AlphanumListbox:
{
Bool befuellt
...
AlphanumListbox LbPLZ
AlphanumListbox ...
}
and this type AlphanumListbox has 3 members:
{
short Anzahl
AlphanumWertepaar *Werte
char AktuelleSchluessel
}
One of them - AlphanumWertepaar *Werte is also a pointer to another substructure of 2 variables of type char[51].
May I resume your advise so, that to fill the whole "GisAbfrage" correct, I have to fill one after another 3 levels of structures:
1-st level: UDT GisAbfrage with 4 long values, as they are pointers to 4 members
2-nd level: each of these 4 members, according to their structures, at that all AlphanumListbox will made of
- int (analog of short)
- long (as a pointer to the structure AlphanumWertepaar)
- byte(1 to 51) (analog of char[51])
3-rd level: all UDT of type AlphanumListbox with 2 arrays of bytes(1 to 51)
?
Yes, you are right about long pointer instead of UDT. To check it I made a dummy dump array (1 to 2000) of bytes and copied there first 2000 bytes beginning from the pointed address. And I got only 8 first bytes filled with values > 0. The following were just zeros.
So, it's clear for me now that I shoud fill all members with its own CopyMemory. But what have I do, if the pointer is somewhere deeper in the structure? I'll explain what I mean:
In GisAbfrage I have a pointer to the member GisDatenSet, that has in its own structure another member of type AlphanumListbox:
{
Bool befuellt
...
AlphanumListbox LbPLZ
AlphanumListbox ...
}
and this type AlphanumListbox has 3 members:
{
short Anzahl
AlphanumWertepaar *Werte
char AktuelleSchluessel
}
One of them - AlphanumWertepaar *Werte is also a pointer to another substructure of 2 variables of type char[51].
May I resume your advise so, that to fill the whole "GisAbfrage" correct, I have to fill one after another 3 levels of structures:
1-st level: UDT GisAbfrage with 4 long values, as they are pointers to 4 members
2-nd level: each of these 4 members, according to their structures, at that all AlphanumListbox will made of
- int (analog of short)
- long (as a pointer to the structure AlphanumWertepaar)
- byte(1 to 51) (analog of char[51])
3-rd level: all UDT of type AlphanumListbox with 2 arrays of bytes(1 to 51)
?
Yes, that would be my understanding... but you only need to do this for the UDTs that contain pointers to other UTDs, or pointers to strings. Many of the Microsoft APIs use LPSTR and it's necessary to do the same for them.
ASKER
This sample from NetDomain.zip of Karl Peterson seems really great and can be a good prototype. I hope tomorrow the test will run OK. :)
Great! Let us know how it goes!
ASKER
so, the reading from C UDT at first glance seems OK, but there are 2 strange behaviours, that I don't understand.
To check the correspondence between created structures & memory containt at least somehow I filled the structures AND also the dump of bytes from the same memory fragments.
(Type str_NN is array of NN bytes)
1. in the substructure "Meldung":
Public Type Meldung
Typ As Byte
Nummer As Integer
Text As str_256
Hilfe As str_51
End Type
...
dim GMeldung as Meldung
And now, when I check the lenght of elements and the length of the whole structure, I've the discrepence in total lenght:
?len(GMeldung.Typ)
1
?lenb(GMeldung.Typ)
1
?len(GMeldung.Nummer)
2
?lenb(GMeldung.Nummer)
2
?len(GMeldung.Hilfe)
51
?lenb(GMeldung.Hilfe)
51
?len(GMeldung.Text)
256
?lenb(GMeldung.Text)
256
?len(GMeldung)
310
?lenb(GMeldung)
312 (!!!)
I think, that it was such called "padding"-alignment, and the size of structure was aligned to fit 4-bytes blocks.
2. Substructure VersionsInfo:
Public Type VersionsInfo
Version As str_6
Zusatz As str_6
DatTabellen As Datum
Beschreibung As str_256
End Type
where:
Public Type Datum
Jahr As Integer
Monat As Integer
Tag As Integer
End Type
Here the C structure Datum was declared as:
typedef struct SDatum
{
short Jahr;
unsigned short Monat;
unsigned short Tag;
} TDatum
(Jahr-Monat-Tag are Year-Month-Day)
For Year in dump I have 211 in the first byte and 7 in the second one. In Datum I see 2003. Have I undertand it correct, that the bytes are stored in memory in inversed order?
211 in binar is (1101 0011)
7 in binar is (0000 0111)
but 2003 is (0000 0111 1101 0011)
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --
So, I'm asking to confirm my abovementioned understanding.
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --
Unfortunately, I continue to get the Acces crashed when I try to run the next function ausfuehrenAbfrage(lngPoint er) :(.
BUT: inspite of it, I would like to close THIS question, and, probably open ANOTHER question today-tomorrow about reverse task - creating the structure to pass to C dll from VB UDT (if I can't solve it alone). That's why I would like to thank ALL who participated in this thread, especially AzraSound and mdougan - guys, you are smart and were very helpful!!
I propose to split the points as following:
mdougan - 300
AzraSound - 150
EDDYKT - 50
Does it seem fair to you or have I change the sharing rate somehow ?
Yuriy.
To check the correspondence between created structures & memory containt at least somehow I filled the structures AND also the dump of bytes from the same memory fragments.
(Type str_NN is array of NN bytes)
1. in the substructure "Meldung":
Public Type Meldung
Typ As Byte
Nummer As Integer
Text As str_256
Hilfe As str_51
End Type
...
dim GMeldung as Meldung
And now, when I check the lenght of elements and the length of the whole structure, I've the discrepence in total lenght:
?len(GMeldung.Typ)
1
?lenb(GMeldung.Typ)
1
?len(GMeldung.Nummer)
2
?lenb(GMeldung.Nummer)
2
?len(GMeldung.Hilfe)
51
?lenb(GMeldung.Hilfe)
51
?len(GMeldung.Text)
256
?lenb(GMeldung.Text)
256
?len(GMeldung)
310
?lenb(GMeldung)
312 (!!!)
I think, that it was such called "padding"-alignment, and the size of structure was aligned to fit 4-bytes blocks.
2. Substructure VersionsInfo:
Public Type VersionsInfo
Version As str_6
Zusatz As str_6
DatTabellen As Datum
Beschreibung As str_256
End Type
where:
Public Type Datum
Jahr As Integer
Monat As Integer
Tag As Integer
End Type
Here the C structure Datum was declared as:
typedef struct SDatum
{
short Jahr;
unsigned short Monat;
unsigned short Tag;
} TDatum
(Jahr-Monat-Tag are Year-Month-Day)
For Year in dump I have 211 in the first byte and 7 in the second one. In Datum I see 2003. Have I undertand it correct, that the bytes are stored in memory in inversed order?
211 in binar is (1101 0011)
7 in binar is (0000 0111)
but 2003 is (0000 0111 1101 0011)
--------------------------
So, I'm asking to confirm my abovementioned understanding.
--------------------------
Unfortunately, I continue to get the Acces crashed when I try to run the next function ausfuehrenAbfrage(lngPoint
BUT: inspite of it, I would like to close THIS question, and, probably open ANOTHER question today-tomorrow about reverse task - creating the structure to pass to C dll from VB UDT (if I can't solve it alone). That's why I would like to thank ALL who participated in this thread, especially AzraSound and mdougan - guys, you are smart and were very helpful!!
I propose to split the points as following:
mdougan - 300
AzraSound - 150
EDDYKT - 50
Does it seem fair to you or have I change the sharing rate somehow ?
Yuriy.
ASKER
Two details were forgotten -
1) the ASCII code for 2 extra symbols (that seems to be a "padding") is 253.
Is it always with this symbol does the structures padding occur ?
2) the size of dump for "VersionsInfo" seems to be 274 bytes - not quite divisible by 4, hm...
1) the ASCII code for 2 extra symbols (that seems to be a "padding") is 253.
Is it always with this symbol does the structures padding occur ?
2) the size of dump for "VersionsInfo" seems to be 274 bytes - not quite divisible by 4, hm...
>>I think, that it was such called "padding"-alignment, and the size of structure was aligned to fit 4-bytes blocks
That is correct. As far as the symbol used for this "padding", I would assume it would consistent, at least in this instance, but I cannot verify for certain.
>>Have I undertand it correct, that the bytes are stored in memory in inversed order?
Yes this is the discrepancy between internal Little Endian and Big Endian. In my projects I am often calling routines to swap byte orders for use.
That is correct. As far as the symbol used for this "padding", I would assume it would consistent, at least in this instance, but I cannot verify for certain.
>>Have I undertand it correct, that the bytes are stored in memory in inversed order?
Yes this is the discrepancy between internal Little Endian and Big Endian. In my projects I am often calling routines to swap byte orders for use.
Hi,
Couple of things... yes, the difference between len and lenb is that it will be aligning the contents on a byte boundry... but that is why you should not use lenb unless the functions you are calling require this alignment.
Your problem with integer in the sdatum structure may be caused by the fact that both Visual Basic's integer and long, under 32-bit operating systems actually take up the same amount of storage, 4 bytes, it's just that VB packs the 2 low order bytes with data for integers and uses all 4 bytes for longs... which is why it is just as efficient under a 32-bit OS to use longs as integers, it's just that integers will be limited in contents to a number that can be represented in 2 bytes as opposed to 4. Len, is probably looking only at the contents, ignoring the binary zeros in the high order bytes and giving you a length of 2.
You should check with Microsoft on the actual storage size of integers, but I believe that you will find that this is true... in which case, you probably need to change the definition of all columns that map to a short to something else... maybe a byte array of 2?
If you have the windows.h file available, you can go and look at the function declarations for the Windows API functions, look for one that references a short data type... then, in your Visual Studio, bring up the Windows API declaration viewer which already shows the translated function declarations... you can see what Microsoft used to translate the short data type to....
As for filling the UDT and sending it to the C DLL, my comment above where I used the VARPTR (undocumented VB function) is how you'd go about creating the structure to send, except before making your call to the DLL, you'd fill the values in your local instances of the structures.
AzraSound got you pretty far on a lot of the CopyMemory stuff, so, I don't mind sharing more points with them.
Couple of things... yes, the difference between len and lenb is that it will be aligning the contents on a byte boundry... but that is why you should not use lenb unless the functions you are calling require this alignment.
Your problem with integer in the sdatum structure may be caused by the fact that both Visual Basic's integer and long, under 32-bit operating systems actually take up the same amount of storage, 4 bytes, it's just that VB packs the 2 low order bytes with data for integers and uses all 4 bytes for longs... which is why it is just as efficient under a 32-bit OS to use longs as integers, it's just that integers will be limited in contents to a number that can be represented in 2 bytes as opposed to 4. Len, is probably looking only at the contents, ignoring the binary zeros in the high order bytes and giving you a length of 2.
You should check with Microsoft on the actual storage size of integers, but I believe that you will find that this is true... in which case, you probably need to change the definition of all columns that map to a short to something else... maybe a byte array of 2?
If you have the windows.h file available, you can go and look at the function declarations for the Windows API functions, look for one that references a short data type... then, in your Visual Studio, bring up the Windows API declaration viewer which already shows the translated function declarations... you can see what Microsoft used to translate the short data type to....
As for filling the UDT and sending it to the C DLL, my comment above where I used the VARPTR (undocumented VB function) is how you'd go about creating the structure to send, except before making your call to the DLL, you'd fill the values in your local instances of the structures.
AzraSound got you pretty far on a lot of the CopyMemory stuff, so, I don't mind sharing more points with them.
ASKER
by the way, does somebody need a Gmail invitation? I can issue at least 5.