I got my DLL compiled correctly, but whenever I use a function of the DLL that has more than 1 parameter I get the VB Error 49, Bad DLL Calling Convention.

Why does it work ok with the ones that have only 1 variable and not the ones that have 2 or more??


Thanks for any comments!

For further info look at the thread 16-bit to 32-bit Conversion. I started a new thread because the other was quite long.


Thanks again!! :)
Posted on 2001-10-24 11:48:20 by mikef
Maybe you have to tell VB which kind of calling convention it should use for your DLL's functions, in the declaration of the functions..
I forgot how to do it as it's a very long time ago since I used VB.. I'll see if I can find it for you.

Thomas
Posted on 2001-10-24 11:58:22 by Thomas
Cool.. thanks for your help.

I am continuing to try until I get your message!!

Thanks again.
Posted on 2001-10-24 11:59:45 by mikef
Hmm I just read at the kb article below that VB wants the DLL functions to be using the STDCALL convention (the default of windows API functions and your own functions (.model flat, stdcall). So your functions do have the right calling convention.
(article is here).
I found another page about the parameter types VB can read and which declaration you need for it here: "Declaring DLL routines in VB". Maybe this is of some use.
The only thing that could be wrong is the decoration of the exported function, but I think masm will do that correctly as I remember creating a DLL for VB without any special settings and it worked like a charm. But since it's years ago since I wrote that I have no idea where I stored it...

Thomas
Posted on 2001-10-24 12:11:07 by Thomas
I checked your LIB from the other thread with dumpbin, your exports are decorated correctly (like _MessageBox@16). It has the same decoration as the windows API libs (kernel32, user32) so you should be able to declare them the same way in VB as you would do with APIs (isn't there an API declaration tool with VB?).
So I think the problem lies in the declaration. I found this somewhere as example of an API declaration in VB:
Declare Sub FloodFill Lib "GDI" (ByVal hDC As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal crColor As Long)

Note that GDI stands for GDI.LIB here, not GDI.DLL. Are you declaring your functions in the same manner?

Thomas
Posted on 2001-10-24 13:02:41 by Thomas
Thanks for the info!

Here is how I have been declaring them:

Private Declare Sub trinfo Lib "trackwx.dll" (rev%)
Private Declare Sub trsetadr Lib "trackwx.dll" (baseaddr%)
Private Declare Sub trchk Lib "trackwx.dll" (baseaddr%, retcode%)
Private Declare Sub trreadpos Lib "trackwx.dll" (counter%, value&)
Private Declare Sub trsetpos Lib "trackwx.dll" (counter%, value&)
Private Declare Sub trclrpos Lib "trackwx.dll" (counter%)
Private Declare Sub trsts Lib "trackwx.dll" (otstatus%)
Private Declare Sub trhld Lib "trackwx.dll" (holdbyte%)
Private Declare Sub trsync Lib "trackwx.dll" (bitnum%, polarity%)
Private Declare Sub trreadall Lib "trackwx.dll" (m1&, m2&, m3&, m4&)

I am going to implement yours and see if it works. Thanks again.
Posted on 2001-10-24 13:13:45 by mikef
You can use both "trackwx.dll" as "trackwx" after Lib, it will work both. Two things I can see so far:
1.
Make sure you use the right parameter types:


FIGURE 10: Visual Basic arguments and appropriate declarations

Argument Declaration
-----------------------------------------------------------------
A standard C string (LPSTR, char far *) ByVal S$
A Visual Basic String (see note) S$
An integer (WORD, HANDLE, int) ByVal I%
A pointer to an integer (LPINT, int far *) I%
A long (DWORD, unsigned long) ByVal L&
A pointer to a long (LPDWORD,LPLONG,DWORD far *) L&
A standard C array (A[]) Base type of
Array
A Visual Basic array A()
A structure (user-defined TYPE) S As Struct
-----------------------------------------------------------------
End of Figure 10


2. If your proc returns a value, use Declare Function instead of Declare Sub.

Thomas
Posted on 2001-10-24 13:20:02 by Thomas
Thanks for the help. I saw that article this morning in the knowledge base.

I still can't get it too work on the procedures with more that 1 variable....

The trinfo routine now return 63357 instead of -2158... or something like that.

I think there may be problems with the ASM code.

I put it in the 16-bit to 32-bit conversion thread, but noone made any real specific comments to the ASM dll code.


thanks again for eveything. I hope I can be of some help to you oneday. :)
Posted on 2001-10-24 13:46:42 by mikef
I really think it has to do with the parameter declaration. Your exports according to dumpbin are:


Exports

ordinal name

_trchk@8
_trclrpos@4
_trhld@4
_trinfo@4
_trreadall@16
_trreadpos@8
_trsetadr@4
_trsetpos@8
_trsts@4
_trsync@8

These exports are correct, but I doubt if this is correct:
counter%, value&.
The % after counter means that you are passing the pointer to an integer instead of the integer itself. Is this correct? I think this is the problem.

To get the right return values, I think you should add the return type to the declaration:
declare blahblah As Long
or something like that, Long is the type of the return value.

63357 instead of -2158...

If you mean 63378 and -2158, then VB thinks the return value is an unsigned 16-bit value, instead of a signed 32-bit value (or are you returning a 16 bit value?).
(edit: because -2158 seen as unsigned value is 63378)
I have no VB nor docs about VB here so I can't look it up easily..

Thomas
Posted on 2001-10-24 13:55:55 by Thomas
I see. That makes good sense now. Sorry you have to explain in such detail, but this is my first ASM experience. I am a newbie for sure.

I will try some more with the VB. I think you are right, it must be in the Declares.

I did mean 63378 and -2158....I made a typo.

That makes sense as well.

In the DLL it has both 16 and 32...I think.

Here is the routine for the trinfo that is returning the above numbers:

;**********************************
trinfo PROC USES ESI EDI EAX EBX ECX EDX,drvrevptr:dword
;**********************************
L2P16 drvrevptr,drvrev
ret

trinfo endp


And here is the L2P16 macro:

L2P16 Macro passedptr,local16
mov AX,local16
mov eax, passedptr
mov ,AX
Endm

I also have macros nameed P2L16, L2P32, P2L32. I guess 2 are for 16-bit and 2 are for 32-bit.

Is this acceptable???


Thanks so much!!
Posted on 2001-10-24 14:05:02 by mikef
Here is my ASM code including .ASM .DEF and the .DLL itself incase anyone wants to take a look.

We have come to a conclusion that it must be the VB declares, so I have posted all my code if anyone is interested in looking.


Thanks to everyone for their excellent help!

:) :alright:
Posted on 2001-10-24 14:08:52 by mikef
PROC USES ESI EDI EAX EBX ECX EDX

You only need to save esi, edi and ebx. It's not necessairy to save the other registers, and you should definitely not save EAX as the procedure returns it's returnvalue in EAX...

Btw, what's this dll for? CD playing? Is it's original source DOS code? As it's using in and out instructions which you shouldn't use in windows (although they work in win9x).

I don't know what trinfo is meant to do, but right now it does this:
It takes the first word from the drvrev array, and stores it into a variable pointed to by passedptr. Is that what it should do?
In that case, the declaration you used, Private Declare Sub trinfo Lib "trackwx.dll" (rev%), is correct.

But I would like to know what you want to do with the DLL..

Thomas
Posted on 2001-10-24 14:28:21 by Thomas
This DLL is for an OptiTracker PC card that our company sells.

My company is MicroKinetics Corp in Georgia, USA.

www.microkinetics.com

We are a very small firm (8 people) and I am the software engineer. We have 1 sale person, 1 mechincal engineer, 1 electrical engineer, a secretary, myself, an accountant and 2 electrical assemblers.

This DLL was written for Win3.x.

The OptiTracker PC card goes into an ISA slot in your machine... then you hook a DB25 cable to it and then to an encoder, which keeps track of exact position of stepper motors that control our lathes and mills.

And, Yes, the trinfo is just supposed to return the revision of the DLL. That is all the trinfo does.

I just cannot get VB to get the information from the DLL. You are right.. it MUST be the declares.

Also this DLL will only be for Win9x machines... as our Windows software works only with Win98... but our Device driver works with Win9x.

Also, does that mean that in my USES statement I should only use esi, edi and ebx like so:

PROC USES ESI EDI EBX

instead of my original:

PROC USES ESI EDI EAX EBX ECX EDX


Thanks again so much for helping me. I have been working day in and day out to figure this out.

:)
Posted on 2001-10-24 14:52:06 by mikef
Also, does that mean that in my USES statement I should only use esi, edi and ebx like so:

Yes that's right.

Maybe you should place some msgboxes to display values inside your code to check if your dll is working correctly, then you know if the problem is on the asm side or the vb side..

Thomas
Posted on 2001-10-24 15:09:36 by Thomas
Another thing you could do is start with a new empty dll, and add some really simple functions like:


AdditionFunc proc dwValue1:DWORD, dwValue2:DWORD, lpDestination:DWORD
mov eax, dwValue1
add eax, dwValue2 ;add value 1 and 2
mov ecx, lpDestination
mov [ecx], eax
ret
AdditionFunc endp


And then try to make it work in VB:
(untested code)


Declare Sub AdditionFunc Lib "simpledll" (ByVal dwValue1&, ByVal dwValue2&, result&)
' dwValue1 and 2 are directly passed, result indirectly with a pointer to a long.

Dim value1 As Long
Dim value2 As Long
Dim result As Long

value1 = 10
value2 = 20
AdditionFunc value1, value2, result
MsgBox "Result is: " & result


Then if you get an idea of how to declare variables the way you want, apply it onto the code you already have. First recreate the layout of the functions from scratch, use some simple values to check if they work, then copy the real code from your current file to the new procedure and fix the errors..
That's my suggestion for a good approach..

Thomas
Posted on 2001-10-24 15:52:12 by Thomas
I will try that. Thanks again so much for all of your help.

:) :alright:
Posted on 2001-10-24 16:00:06 by mikef
Posted on 2001-10-24 19:28:59 by sluggy
Your specific problem with the VB declares was that you were not including the name decoration in the declare, so VB could not match the name with an ordinal.

I.e., if a function is exported as:
_trchk@8

then your VB declare should look like this:
Private Declare Sub trchk Alias "_trchk@8" Lib "trackwx.dll" (baseaddr%, retcode%)

To get rid of the decoration, you need to make a dll export file (.def file), which would look something like this:



LIBRARY <your dll name>
DESCRIPTION "<desc of your dll>"

EXPORTS
Function1 @1
Function2 @2
<etc>



You also have a problem with the way you are passing your parameters. "baseaddr%" means baseaddr is an int, and because you didn't specifically say "ByVal", VB automatically passes it ByRef. This is a real problem when it comes to strings, as strings should ALWAYS be passed ByVal (otherwise your function will end up receiving a pointer to a BSTR string). So your function declare should look like this:

Private Declare Sub trchk Alias "_trchk@8" Lib "trackwx.dll" (ByVal baseaddr as Integer, ByVal retcode as Integer)

One last thing: you are complaining about getting negative numbers back from your asm function. You have to remember that VB does not have the concept of unsigned longs, so once your long variable gets past the value 2,147,483,647, VB automatically treats it as negative. The important thing to remember is that internally the number is still the same, just VB treats it differently. Therefore, -1 in VB is actually the same as &HFFFFFFF in VB.
Posted on 2001-10-24 19:31:48 by sluggy
sluggy I think you are not totally right. I had looked at the created dll and though the exported procs are STDCALL they have no decoration (maybe thats because they were listed in the .def file?). Therefore I would prefer not to change the VB side but the ASM side.

mikef, I have changed your last ASM source and created a DLL. The exports of this DLL have the correct decoration. No DEF file is needed. To create do:

ml -c -coff track3.asm
link track3.obj /subsystem:windows /out:track3.dll

BTW, I think your macro P2L32 is wrong.
Posted on 2001-10-25 04:10:32 by japheth

sluggy I think you are not totally right. I had looked at the created dll and though the exported procs are STDCALL they have no decoration (maybe thats because they were listed in the .def file?). Therefore I would prefer not to change the VB side but the ASM side.

I made my last answer without actually looking at the source, i gathered my info from all the previous posts. You are correct, there is no name decoration happening. And it appears that passing the VB params ByRef is correct. However, it doesn't explain why mike is passing a 16 bit value (VB Integer) to a function that is expecting a 32 bit value (DWORD).
Posted on 2001-10-25 05:33:47 by sluggy