Solved

Pass VB structure to C DLL

Posted on 1998-05-13
18
258 Views
Last Modified: 2012-06-27
I am receiving this error "run-time error 49" bad DLL calling convention.

I am calling a  DLL writen in C.  The DLL expects a LONG and then a structure with 4 ints and one char *.  

In VB:
I have declared the function as follows;
Declare Function dllname Lib "api.dll" Alias "_dllname @24" (ByVal val1 As Long, ByVal options As Options) As Integer

I have declared the structure as follows;
Type Options
    var1                       As Integer
    var2                       As Integer
    var3                       As Integer
    var4                       As Long
    var5                       As Integer
End Type

With the string data in var4, I convert the  UNICODE string to ANSI.  To do this, I create an array of byte characters.
The string convert function (StrConv) will do this.  To get the pointer to a memory address in VB, I use an undocumented function in VB called
(VarPtr) that returns the memory address of the pointer.

I then set this to the Long variable in the var4 in the structure.

When I call the function in the C DLL, I get the above mentioned error. Any thoughts as to why?

SI
0
Comment
Question by:sirwin
  • 6
  • 4
  • 3
  • +3
18 Comments
 
LVL 6

Expert Comment

by:clifABB
ID: 1447450
I assume you're using Win95 or WinNT.

The problem is that, to C++, an integer is four bytes and a long is eight bytes.

While you can pass a long where C expects an integer, you need to pass a structure of two longs where C expects a long:

Type C_Long
  Low  As Long
  High As Long
End Type
0
 

Author Comment

by:sirwin
ID: 1447451
I don't follow this logic.  C should be expecting LONG holding the memory address of the PTR.  (Souldn't it).  How do I declare the variable in the structure?  what do I add to the second LONG variable that has been added?
0
 
LVL 6

Expert Comment

by:clifABB
ID: 1447452
When you declare the C_Long structure, the two four byte longs are contiguous allowing C to see it as an eight byte memory location.

Here is what your declares should look like:
Type C_Long
  Low  As Long
  High As Long
End Type

Type Options
    var1                       As Long
    var2                       As Long
    var3                       As Long
    var4                       As C_Long
    var5                       As Long
End Type
Declare Function dllname Lib "api.dll" Alias "_dllname @24" (ByVal val1 As C_Long, ByVal options As Options) As Long
0
 

Author Comment

by:sirwin
ID: 1447453
This still does not work.  One problem that I noticed is that I have to pass user defined Type ByRef and not ByVal.  Therfore, this should read ByRef options As Options.  I have passed val1 (first vaiable outside the structure) as Long to other C DLLs without a problem so I don't know why this would need to change.

The C DLL is expecting;
val1 as int
var1 as int
var2 as int
var3 as int
var4 as char*
var5 as int

All var# are in a structure.
0
 
LVL 2

Expert Comment

by:chris_a
ID: 1447454
Are you sure about a long being 64 bit, ie 2 longs, I thought that Win32 brought in long int = int, 64 bit ints are declared as _int64.

For this sort of thing I generally copy the numbers into a string in VB and pass the string.
0
 

Author Comment

by:sirwin
ID: 1447455
But if you pass as a string, the C API is looking for a char* and will still need the memory location (prt) of the string.  This would be invalid type.
0
 
LVL 2

Expert Comment

by:chris_a
ID: 1447456
You don't pass the string, you pass the pointer, it works, I have done it, usually the problem is in returning a string, you have to use space(x) to allocate the space first.

Also the struct declared in C++ may be optimized so each element falls on a 4 byte boundary, use pragma pack(1) to stop this for the structure.
0
 
LVL 4

Expert Comment

by:yowkee
ID: 1447457
sirwin,
  You have no need to use Varptr or Strptr to pass the string. Most of the time VB will convert for you.Try declare the type Option as:

Type Option
  Var1 as Long
  Var2 as Long
  Var3 as Long
  Var4 as String
  Var5 as Long
End Type

  When you assign any string to Var4, add a vbNullChar to the end of the string(since C expect a null terminated string). eg.
  Dim op as Option
  op.Var4 = "Testing" + vbNullChar

  Then pass it to the DLL function.
  Hope this help.

 


0
 
LVL 4

Expert Comment

by:yowkee
ID: 1447458
 Another question, do you write the C function in DLL? Since your error appear as "DLL calling convention", the C function must be exported with standard call ( put a "WINAPI" in front of the function, other calling convention will cause the error).
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 1

Expert Comment

by:dnavarro
ID: 1447459
>The C DLL is expecting;
      var1 as int
      var2 as int
      var3 as int
      var4 as char*
      var5 as int <

TYPE C_TYPE
  var1 AS LONG
  var2 AS LONG
  var3 AS LONG
  var4 AS LONG   '<-- pointer to string data
  var5 AS LONG
END TYPE

var4 = AddressOf(AnsiString)   '<-- Use AddressOf in VB5 to get the address of the string data

--Dave
0
 

Author Comment

by:sirwin
ID: 1447460
Dave,

The "AddressOf" Operator will give you a memory address of a procedure not a string.  This is used in call backs.

sirwin
0
 
LVL 1

Expert Comment

by:dnavarro
ID: 1447461
You would need to use StrPtr() (if VB supports it) or a third-part DLL/Control to get the addres of the string data.  If VB doesn't support StrPtr(), let me know and I'll create a small DLL for you that will return the address of any variable passed to it.

--Dave
0
 
LVL 4

Expert Comment

by:yowkee
ID: 1447462
sirwin,
  Do you write the C DLL? Could you show your declaration of the export function _dllname@24 in your C code?
0
 
LVL 1

Expert Comment

by:dnavarro
ID: 1447463
I don't program in C.  I was actually going to create the DLL in PowerBASIC and email it to you if you wanted one that returned a data pointer.

--Dave
0
 

Author Comment

by:sirwin
ID: 1447464
EntLog EXPORT dllname(const val1 ctx,  const OPTIONS Options).

I think I know what is wrong.  This should be;

EntLog EXPORT dllname(const val1 ctx,  const OPTIONS* Options)

Since VB can only pass UDT ByRef, the C DLL must receive a ptr.  I will have to test but it should work.  The only immediate problem is that I cannot make immediate changes to the C code.

Options:

1.  Is there anything in VB that I can do to work around this?
2.  I can write another C DLL that receives this ByRef and then pass to the ByVal C DLL.

Any thoughts?


0
 
LVL 4

Expert Comment

by:yowkee
ID: 1447465
1. Bad DLL calling convention concerned about how the dll exported function being called. If I am not wrong, in standard calling convention (__stdcall or WINAPI in front of function name), should be the function clear the parameter in stack before return to caller. So, in this case, I don't think you could do anything in VB to workaround this.

2. I am not sure about this. How your another DLL recognize UDT Option? Did the type OPTIONS stay in global namespaces? It should be pass by pointer even in different C DLL.

If change the passing UDT to pass by pointer still not worked, check you definition of EXPORT or maybe put EXPORT Entlog....

Hope this info help. :)
0
 

Accepted Solution

by:
Darlof earned 100 total points
ID: 1447466
Since the last activity on this was five days ago, I'll take a stab at it.
I'm going from memory on this, but at my last job we had a similar problem.
Here are some possibilities:
[1] VB supports the standard Microsoft StdCall calling convention (If you aren't using MS Visual C, check in mixed-language section of your C compiler)
[2] Your DLL functions should be defined as extern "c" in the header file (use the stdcall specified to choose the calling convention) and exported in the .DEF file. This also prevents C++ name mangling from occurring.
[3] In Win32, DLL names are case sensative
[4] User defined types can be called by reference only (as noted in the `comment conversation` above)
[5] User defined structures passed to DLLs use a structure member alignment of 4 (try changing all integers to longs in both your dll and vb code, or if that isn't possible redeclare the structure in VB as three longs)
0
 

Author Comment

by:sirwin
ID: 1447467
Excellent suggestions.  Most if not all helped me get this working.  
I declared the UDT as Long, long, Long, String, Long. The other problem I had was that the C function was expecting the UDT ByVal.  I had to write another C DLL to accept it ByRef and then pass it to the exisiting C DLL function ByVal.

thanks for the help.
Steve
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

There are many ways to remove duplicate entries in an SQL or Access database. Most make you temporarily insert an ID field, make a temp table and copy data back and forth, and/or are slow. Here is an easy way in VB6 using ADO to remove duplicate row…
If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
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…

762 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

18 Experts available now in Live!

Get 1:1 Help Now