Solved

Calling dll from VBA and getting a big complex UDT as a return value - "bad calling convention" error.

Posted on 2004-10-08
28
580 Views
Last Modified: 2012-05-05
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(void);

TBool API_EXPORT ausfuehrenAbfrage(TGisAbfrage*);

TBool API_EXPORT loescheAbfrage(TGisAbfrage*);

TBool API_EXPORT loescheAbfrageStruktur(TGisAbfrage*);

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(GAbfrage)
    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.

0
Comment
Question by:inversojvo
  • 14
  • 7
  • 6
  • +1
28 Comments
 
LVL 28

Expert Comment

by:AzraSound
ID: 12261099
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.
0
 
LVL 26

Assisted Solution

by:EDDYKT
EDDYKT earned 50 total points
ID: 12261242
Change all


String
to
byte


ie

String * 256
to
byte * 256
0
 
LVL 18

Expert Comment

by:mdougan
ID: 12261287
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!
0
 

Author Comment

by:inversojvo
ID: 12262321
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.

0
 
LVL 18

Expert Comment

by:mdougan
ID: 12263361
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.
0
 

Author Comment

by:inversojvo
ID: 12274605
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(GAbfrage)
    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(TGisAbfrage*);

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?

0
 
LVL 28

Assisted Solution

by:AzraSound
AzraSound earned 150 total points
ID: 12276016
Well, once you get a pointer back, my thought is that you then need to copy the data pointed to into a variable of the appropriate type (structure), e.g.,


Dim GAbfrage As GisAbfrage
Dim lngPointer As Long

'get pointer and then copy to structure
lngPointer = erzeugeAbfrageStruktur()
CopyMemory ByVal GAbfrage, lngPointer, LenB(GAbfrage)

blnTmp = ausfuerenAbfrage(GAbfrage)
'etc.


The actual CopyMemory call may have to be altered a bit, I can't recall off hand how to properly copy pointer to a type, but that is the general idea.  The rest of your function calls should then remain the same now that you have the type structure as you originally required.
0
 

Author Comment

by:inversojvo
ID: 12276534
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)?
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 12276554
After the CopyMemory call, set a breakpoint and look at the contents of the GAbfrage structure and see if it looks correct or not.
0
 

Author Comment

by:inversojvo
ID: 12276612
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 ? :)
0
 

Author Comment

by:inversojvo
ID: 12276677
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).
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 12277494
Error 453 means your function declaration is incorrect?  Which function is it that raises the error?
0
 

Author Comment

by:inversojvo
ID: 12277813
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(TGisAbfrage*);

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":
http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_21163658.html

0
 
LVL 28

Expert Comment

by:AzraSound
ID: 12277862
What about the initial declaration?

Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (GAbfrage As GisAbfrage) As Boolean

Can you call:

b = ausfuerenAbfrage(GAbfrage)
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 

Author Comment

by:inversojvo
ID: 12278049
No, the same error <.. memory "0x00000000" ...> and Access crashes. :(.

Don't you think, that the unproper structure could cause it?
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 12278390
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.
0
 
LVL 18

Accepted Solution

by:
mdougan earned 300 total points
ID: 12278616
OK, I think I see the issue with your GAbfrage structure.

The original struct for this is defined as so:

{
    TMeldung *Meldung;
    TVersionsInfo *VersionsInfo;
    TGisAbfrageParameter *GisAbfrageParameter;
    TGisDatenSet *GisDatenSet;
} TGisAbfrage;


Each of these items are pointers to another structure, therefore, they are really Longs, and not instances of the structure as you've converted the declaration to:

Public Type GisAbfrage
    Meldung As Meldung
    VersionsInfo As VersionsInfo
    GisAbfrageParameter As GisAbfrageParameter
    GisDatenSet As GisDatenSet
End Type

Rather, it should be:

Public Type GisAbfrage
    Meldung As Long
    VersionsInfo As Long
    GisAbfrageParameter As Long
    GisDatenSet As Long
End Type

Then, in your routine, when you declare an instance of these you will have to do this:

Dim MyGisAbFrage as GisAbfrage
Dim MyMeldung as Meldung
Dim MyVersionsInfo as VersionsInfo
Dim MyGisAbfrageParameter as GisAbfrageParameter
Dim MyGisDatenSet as GisDatenSet

MyGisAbFrage.Meldung = VarPtr(MyMeldung)
MyGisAbFrage.VersionsInfo = VarPtr(MyVersionsInfo)
MyGisAbFrage.MyGisAbfrageParameter = VarPtr(MyGisAbfrageParameter)
MyGisAbFrage.GisDatenSet = VarPtr(MyGisDatenSet)

Then, you will be ready to pass the MyGisAbfrage instance to these functions ByRef

Declare Function ausfuerenAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByRef GAbfrage As GisAbfrage) As Boolean
Declare Function loescheAbfrage Lib "D:\Gis\GisAbfrage32.dll" (ByRef GAbfrage As GisAbfrage) As Boolean
Declare Function loescheAbfrageStruktur Lib "D:\Gis\GisAbfrage32.dll" (ByRef GAbfrage As GisAbfrage) As Boolean

By not including ByRef in your declarations, you are getting it as the default anyway, so this is not a problem for you, but I like to put it here explicitly just to be sure.  If any of these three functions modifies the data in the structure that you passed to it, then you should see those changes in your local variables without having to do any copymemory function, since you'd passed the structure by reference, chages were made to the variables that you declared.

Now, the call which returns the GisAbfrage struction is a little more complicated... as you were already figuring out, what is returned is a pointer to a piece of memory which you have to use CopyMemory to get at... however, you have inverted the ByVal on the first and second parameters... your statement should read:

CopyMemory  MyGisAbFrage, ByVal lngPointer,  Len(MyGisAbFrage)

And, when I've used this to copy into UDTs I use Len, not LenB... if you are copying into a string, then you might have to call one or two APIs

lstrlenA
lstrlenW

To get the length of the string

Once you've got your MyGisAbFrage populated... it's only going to be populated with long pointers to the other elements, so, you're going to have to then do

CopyMemory  MyMeldung , ByVal MyGisAbFrage.Meldung,  Len(MyMeldung)
(do the same for the other three members...)

After all of that, you should be able to work with the values in

MyMeldung.Text
MyMeldung.Nummer  etc.

Hope this gets you closer to your solution!

Oh, and by the way, you really should look at the Karl Peterson samples, as that is how I worked most of this out... except that a better sample to look at is the sample called NetDomain.zip

Mike


0
 

Author Comment

by:inversojvo
ID: 12278922
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.



0
 

Author Comment

by:inversojvo
ID: 12278989
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!
0
 

Author Comment

by:inversojvo
ID: 12286027
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)

?

0
 
LVL 18

Expert Comment

by:mdougan
ID: 12286528
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.
0
 

Author Comment

by:inversojvo
ID: 12290024
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. :)
0
 
LVL 18

Expert Comment

by:mdougan
ID: 12290529
Great!  Let us know how it goes!
0
 

Author Comment

by:inversojvo
ID: 12299746
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(lngPointer) :(.

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.
0
 

Author Comment

by:inversojvo
ID: 12299843
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...
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 12299953
>>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.
0
 
LVL 18

Expert Comment

by:mdougan
ID: 12300002
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.
0
 

Author Comment

by:inversojvo
ID: 12300116
by the way, does somebody need a Gmail invitation? I can issue at least 5.
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Introduction While answering a recent question about filtering a custom class collection, I realized that this could be accomplished with very little code by using the ScriptControl (SC) library.  This article will introduce you to the SC library a…
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now