Link to home
Start Free TrialLog in
Avatar of Igor_Shtork
Igor_ShtorkFlag for Kazakhstan

asked on

Pass parameters from VB code to function in my DLL

Hi all!
I have crazy passing parameters from my program in VB to function in DLL (wrote by VC++, Win32 DLL project).

This is a parts of code:

in VB:

Module:
Declare Function Finding Lib "AB.dll" (ByVal P0 As String, ByVal P11 As Integer, ByVal P12 As Integer, ByVal P21 As Integer, ByVal P22 As Integer, ByVal P3 As Boolean, ByVal P4 As Boolean)
Calling function:
Finding "Test", 0, 5, -1, -1, False, True

in C++:
Declaration:
AB_API void Finding (char *P0, int P11, int P12, int P21, int P22, bool P3, bool P4);
Description:
AB_API void Finding (char *P0, int P11, int P12, int P21, int P22, bool P3, bool P4)
{
out=fopen("RESULT.txt","w");
fprintf(out,"%s,%d,%d,%d,%d,%d,%d",P0,P11,P12,P21,P22,P3,P4);
fclose(out);
return;
}

So, when I try run the code, I have result in file RESULT.txt:

,1274212,0,5,-1,255,0

Only this and nothing more... :-)
When I try change the declaration in VB (ByVal to ByRef):
Declare Function Finding Lib "AB.dll" (ByRef P0 As String, ByRef P11 As Integer, ByRef P12 As Integer, ByRef P21 As Integer, ByRef P22 As Integer, ByRef P3 As Boolean, ByRef P4 As Boolean)

I have this result:
,1243904,1243884,1243880,1243876,224,220

I don't understand 2 things:
1. Why do first variable pass nothing?
2. Why is there very big values pass in first integer in first example and all integers in second? What does it mean???
Avatar of Norbert
Norbert
Flag of Germany image

I don't know what happens but there seems to be a shift inside your parameters:
,   <--- This should be the string but
1274212, <--- this seems to be the pointer to the string
0,  <--- This is the First integer parameter
5, <--- this is the second integer parameter
-1, <--- this is the third integer parameter
255, <--- this should be the first bool parameter but it is the 4th integer parameter because -1 is 0xffff
bool is defined as unsigned char and 0xff = 255
0  <--- this is the first Bool parameter because false is well defined as 0

the second bool parameter is lost

if you call ByRef that means that you are passing pointers
so the result is like I would expect you print out the pointer values

how did you define AB_API
Avatar of Igor_Shtork

ASKER

I define it ease:

#define AB_API __declspec(dllexport)
Avatar of inpras
inpras

Hi Igor
I have tried here its working fine my AB_API is extern "C" BOOL PASCAL EXPORT
check whether URs is same
Hope this helps
inpras
Yes I think that is the problem
My new declaration:
extern "C" BOOL PASCAL EXPORT void Finding (char *P0, int P11, int P12, int P21, int P22, bool P3, bool P4);


I tried it, but compiler send a lot of errors:
c:\compil\vb6\msdev98\myprojects\ab\ab.h(27) : error C2144: syntax error : missing ';' before type 'void'
c:\compil\vb6\msdev98\myprojects\ab\ab.h(27) : warning C4229: anachronism used : modifiers on data are ignored
c:\compil\vb6\msdev98\myprojects\ab\ab.h(27) : fatal error C1004: unexpected end of file found

The 27th string is declaration of Finding function
function cannot be both BOOL and void

remove the BOOL.


this is basically what you need...

extern "C" void __declspec(dllexport) __stdcall Finding(char *P0, int P11, int P12, int P21, int P22, bool P3, bool P4);

PASCAL = __stdcall
EXPORT = __declspec(dllexport)

regards,
Mike.
extern "C" void PASCAL EXPORT Finding (char *P0, int P11, int P12, int P21, int P22, bool P3, bool P4);

Sorry that was a typo

Unfortunatuly, when I tried declare by:

extern "C" void PASCAL EXPORT Finding (char *P0, int P11, int P12, int P21, int P22, bool P3, bool P4);

I have the message in compilator:
c:\compil\vb6\msdev98\myprojects\ab\ab.h(29) : error C2146: syntax error : missing ';' before identifier 'Finding'
c:\compil\vb6\msdev98\myprojects\ab\ab.h(29) : warning C4229: anachronism used : modifiers on data are ignored
c:\compil\vb6\msdev98\myprojects\ab\ab.h(29) : error C2182: 'EXPORT' : illegal use of type 'void'
c:\compil\vb6\msdev98\myprojects\ab\ab.h(29) : fatal error C1004: unexpected end of file found

When I tried add this defenition:
#define PASCAL __stdcall
#define EXPORT __declspec(dllexport)

I have a lot of warnings (anahronisms and etc) and old result in RESULT.TXT file:
,1274212,0,5,-1,255,0
inpas, I think that you use any another defenition of EXPORT and PASCAL. Is it possible or not?
I try make it in VC++ 6.0 (Visual Studio '98). Is it possible, you tried it in another version of VC++?
Then find equuivalent may be extern "c" _declspec(dllexport) void MyFunc(....);

I am working on VC 5
Hope this helps
You must have __stdcall mechanism if you are going to call from VB.

The version I gave earlier works - it is taken from a DLL I wrote for VB users to access our product.

regards,
Mike.

Nothing... :-((
So, I tried easeless test function:
in VB:
Declare Function Test Lib "AB.dll" (A As String)

in C++:
declaration:
extern "C" _declspec(dllexport) void Test(char *A);

extern "C" _declspec(dllexport) void Test(char *A)
{
      out=fopen("RESULT.txt","w");
      fprintf(out,"%s",A);
      fclose(out);
}

The result is file RESULT.TXT 0 byte size. :-((
Sorry, but with __stdcall too.
ASKER CERTIFIED SOLUTION
Avatar of jhance
jhance

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
BTW, the above syntax is really confusing....  I usually do this in the header file:

#define DLLAPI extern "C" __declspec(dllexport)

#define DLLCALLTYPE __stdcall


then the function looks better as:

DLLAPI void DLLCALLTYPE Finding(...);

Pass by value
Hi all! Thanks a lot for your information, but I solved the problem yestoday evening at home.

The declaration, which work good is:
__declspec(dllexport) void Finding(char *A, char *P0, int P11, int P12, int P21, int P22, bool P3, bool P4);

I see, that the crazy solution, but it's working... Now I don't see what is the first NULL parameter, which do pass from VB code. So, I don't delete the question and now I want undestand the feature. Can anybody tell me why the last my declaration is working and old isn't working? What is the first parameter? Is it NULL all time or not?
Sorry, I didn't tell, that *A don't use in DLL. This is a single empty parameter.
This is working from VB without __stdcall declaration????



Exactly! :-) It's working very good! I have this defenition:

#define AB_API __declspec(dllexport)

and the function's defenition is:

AB_API void Finding (char *A, char *P0, long P11, long P12, long P21, long P22, bool P3, bool P4);

I don't know, it's possible, that VC++ add any anouther defenition automaticaly??? :-0
>Exactly! :-) It's working very good! I have this defenition:

I doubt it!!!

If you put this call in a loop and run it for a while you'll find that it leaks memory!  Think about what you've done here.  VB ALWAYS calls using __stdcall, a.k.a. PASCAL calling convention.  The rules for __stdcall and what VB expects are:

1) The FIRST argument is the count of the arguments
2) The called function pops the data off the stack before it returns.

#1 is why your code now works with the dummy argument.  VB is passing the argument count but your C++ function doesn't need it.

Since your function, however, is __cdecl:

1) There is no argument count
2) The calling function cleans the stack when the function returns.

#2 is why this will now leak memory.  Ever time you call this VC++ function, VB calls it with __stdcall and it expects the called function to return after having popped all the arguments off the stack.
Ok! I can accept the answer, but...

1. I tried call the function from VB in loop 10000 times. Is it a lot or not? It was working all times without any errors. :-)

2. The function declarated EXACTLY as I wrore last time. I don't know, it's possible, VC++ 6.0 add automaticaly (???) any additional defenition such as
__cdecl or __stdcall, but I didn't do it myself. I'm sure! :-)

3. I think, that the FIRST parameter is always null string (char * ptr to string equal null string). If you are right, it's not possible. Are you right or I'm right because?

I can accept your answer after you answer to this my questions.
this has got to lead to stack corruption
at some point - i don't fancy your chances if you add and call another
function in your dll.

are you loading and unloading the dll
each time you call your function?

regards,
Mike.
Sorry, I don't know anything about loading/unloading DLL. I tried call it from Basic about this:

for i=0 to 10000
  Finding "Test", 1,1,1,1, True, True
next

I change in C++ code about this:
Finding(...)
{
   fprintf(out,"%s,%d,%d,%d,%d,%d,%d/n" P0, P11, P12, P21, P22, P3, P4);
}


I have a good result in output file and nothing errors in process.
>1. I tried call the function from VB in loop 10000 times. Is it a lot or not?

The default size for the stack (where this data is being left) is 1MB.  So 10000 times will not generally overflow it.  I just disagree with knowingly writing a part of a program that leaks memory.  It _WILL_ come back and bite you eventually!  It's just bad software design practice.

>it's possible, VC++ 6.0 add automaticaly (???) any additional defenition such as __cdecl or __stdcall,

VC++ uses __cdecl by default.  The Windows API functions are all __stdcall by default.  VC++ is comfortable with either one but you MUST specify the correct calling convention when the function is declared or VC++ will generate incorrect code for the call.  If you "magically" forced VC++ to use __stdcall for everything, the C runtime library would fail since it uses __cdecl for everything.

>3. I think, that the FIRST parameter is always null string

After looking at VC++ code, I see that they don't pass the argument count in as the first param on the stack.  (This _IS_ don't on some "pascal" calling conventions but it appears not on VC++).  Anyway, the memory leakage problem remains.  Look at the following example code:

1) I define two functions:

void __stdcall STDTEST(int a, int b)
{
      return;
}

void __cdecl CDECLTEST(int a, int b)
{
      return;
}

2) I called them both from my program:

      STDTEST(1, 2);
      CDECLTEST(1, 2);

3) Look at the code generated in VC++ to call them:

; 128  :       STDTEST(1, 2);

  0001d      6a 02             push       2
  0001f      6a 01             push       1
  00021      e8 00 00 00 00       call       ?STDTEST@@YGXHH@Z      ; STDTEST

; 129  :       CDECLTEST(1, 2);

  00026      6a 02             push       2
  00028      6a 01             push       1
  0002a      e8 00 00 00 00       call       ?CDECLTEST@@YAXHH@Z      ; CDECLTEST
  0002f      83 c4 08       add       esp, 8

They look almost identical EXCEPT for that little "esp, 8" at the end of the __cdecl function.  That drops the top 8 bytes from the stack which corresponds to the 2, 4-byte int values I pass into the function.

If you look at the code for the __stdcall function itself (not posted here) you will see that it ends with a "ret 8" instruction that returns to the caller with 8 subtracted from the stack pointer.

In pure VC++, I cannot call the __cdecl function using a __stdcall calling convention since the compiler itself catches this error and won't let me do it.  From VB, however, this error can happen since VB ASSUMES that all calls it makes to the DLL are __stdcall and it processes the stack accordingly.

So in your code, VB is calling with __stdcall and expecting your function to clean up it's stack.  But since the VC++ code is __cdecl, it's not going to do that, it's expecting the calling function to clean up for it.  In your example, every time you call the function, you are leaving 32 bytes on the stack.

Thanks a lot for your explanation, jhance! I tried call the function with your defenition and without it, but I didn't see any difference. I'll use it now. Sorry, I hadn't full undestanding the mechanism before.
So, I haven't good experience in VC++ and I didn't use DLL before too. I had doubt in your explanation because I didn't understand this. Thanks!
Glad to have been a help.  Understanding the difference between __stdcall and __cdecl is something that not that many Windows programmers do understand.  In most cases you just don't have to worry about it.  It's only when you start mixing code from development environments that use DIFFERENT default calling conventions that you need to know this.
Ok, jhance! Anyways I really glad to receive new additional inform. If it is prossible, can you write me your e-mail or send it to IgorS@terralink.almaty.kz?
So, I have one very small question, I'm sure, that it's very easy for you. I won't discuss it here.
Thanks a lot again! :-)