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?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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
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
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

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
DarlofCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.