Pass VB structure to C DLL

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
sirwinAsked:
Who is Participating?
 
DarlofConnect With a Mentor Commented:
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
 
clifABBCommented:
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
 
sirwinAuthor Commented:
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
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

 
clifABBCommented:
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
 
sirwinAuthor Commented:
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
 
chris_aCommented:
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
 
sirwinAuthor Commented:
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
 
chris_aCommented:
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
 
yowkeeCommented:
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
 
yowkeeCommented:
 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
 
dnavarroCommented:
>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
 
sirwinAuthor Commented:
Dave,

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

sirwin
0
 
dnavarroCommented:
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
 
yowkeeCommented:
sirwin,
  Do you write the C DLL? Could you show your declaration of the export function _dllname@24 in your C code?
0
 
dnavarroCommented:
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
 
sirwinAuthor Commented:
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
 
yowkeeCommented:
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
 
sirwinAuthor Commented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.