Bad DLL calling convention

R

RB Smissaert

Made a C++ dll with MS VC6 and trying to call the dll from Excel VBA.

This is the code in the .cpp file:


#include "stdafx.h"
#include <string>
#include <math.h>

using namespace std;

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}


//to compare case in-sensitive
//----------------------------
int CompareIgnoreCase(const string& s, const string& s2)
{
string::const_iterator p = s.begin(),
p2 = s2.begin();

while(p!= s.end() && p2!= s2.end()){
if(tolower(*p)!= tolower(*p2))return tolower(*p)< tolower(*p2) ? -1: 1;
++p; ++p2;
}
return s2.size()- s.size();
}


//round number n to d decimal points
//----------------------------------
double fround(double n, unsigned d)
{return floor(n * pow(10, d) + 0.5) / pow(10, d);}


double _stdcall Framingham2 (long Age,
long SysBP,
double TotChol,
double HDL,
string Sex,
string Diabetes,
string LVH,
string Smoke,
long Years = 10)


{
int x;
int btSex;
int btDiabetes;
int btLVH;
int btSmoke;
string sFEMALE = "FEMALE";
string sYES = "YES";
double dMu;
double dS;
double dU;
double dProb;

//exclude out of range input
//--------------------------
if ((Age < 35) ||
(Age > 75) ||
(SysBP < 50) ||
(SysBP > 300) ||
(TotChol < 1) ||
(TotChol > 30) ||
(HDL < 0.2) ||
(HDL > 10) ||
(Years > 10))
return 0;

x = CompareIgnoreCase(Sex,sFEMALE);
if (x == 0)
btSex = 1;
else
btSex = 0;

x = CompareIgnoreCase(Diabetes,sYES);
if (x == 0)
btDiabetes = 1;
else
btDiabetes = 0;

x = CompareIgnoreCase(LVH,sYES);
if (x == 0)
btLVH = 1;
else
btLVH = 0;

x = CompareIgnoreCase(Smoke,sYES);
if (x == 0)
btSmoke = 1;
else
btSmoke = 0;

dMu = 15.5305 +
28.4441 * btSex +
-1.4792 * log(Age) +
-14.4588 * log(Age) * btSex +
1.8515 * pow(log(Age), 2) * btSex +
-0.9119 * log(SysBP) +
-0.2767 * btSmoke +
-0.7181 * log(TotChol / HDL) +
-0.1759 * btDiabetes +
-0.1999 * btDiabetes * btSex +
-0.5865 * btLVH;

dS = exp(0.9145 + (-0.2784 * dMu));
dU = (log(Years) - dMu) / dS;
dProb = 1 - exp(-exp(dU));

return fround(dProb * 100,1);
}

I have included a .def file with this:

EXPORTS
Framingham2

It compiles (win32 release) nicely without any errors or warnings.
The function also shows nicely when I run DEPENDS.EXE.
I have checked the function in a C++ console app and that runs fine giving
the right
results. This is without input arguments though and with the values
hard-coded.



In Excel VBA I do this:

Option Explicit
Private Declare Function Framingham2 _
Lib "C:\Program Files\Microsoft Visual
Studio\MyProjects\Framingham\Release\Framingham.dll" _
(ByVal Age As Long, _
ByVal SysBP As Long, _
ByVal TotChol As Double, _
ByVal HDL As Double, _
ByVal Sex As String, _
ByVal Diabetes As String, _
ByVal LVH As String, _
ByVal Smoke As String, _
ByVal Years As Long) As Double


Sub test()

MsgBox Framingham2(50, 140, 5.6, 1.6, "male", "no", "no", "no", 10)

End Sub

This compiles fine, but when I run Sub test() I get Run-time error 49: Badd
DLL calling convention.
I have tried altering the datatypes both in the VBA declaration and in the
..cpp file but no success.
I am completely new to this, so I am sure I have done something stupid
somewhere, but I just can't find it.
Thanks for any advice.


RBS
 
J

Jakob Bieling

RB Smissaert said:
Made a C++ dll with MS VC6 and trying to call the dll from Excel VBA.

You are off-topic here, as you do not have a problem with C++, but
the your particular environment. Try comp.os.ms-windows.programmer.win32
instead.

I will answer anyway, see below for the off-topic-ness ;)
double _stdcall Framingham2 (long Age,
long SysBP,
double TotChol,
double HDL,
string Sex,
string Diabetes,
string LVH,
string Smoke,
long Years = 10)
Private Declare Function Framingham2 _
Lib "C:\Program Files\Microsoft Visual
Studio\MyProjects\Framingham\Release\Framingham.dll" _
(ByVal Age As Long, _
ByVal SysBP As Long, _
ByVal TotChol As Double, _
ByVal HDL As Double, _
ByVal Sex As String, _
ByVal Diabetes As String, _
ByVal LVH As String, _
ByVal Smoke As String, _
ByVal Years As Long) As Double

This compiles fine, but when I run Sub test() I get Run-time error
49: Badd DLL calling convention.
I have tried altering the datatypes both in the VBA declaration and
in the .cpp file but no success.

A 'string' in C++ is a C++ data type. This data-type has most
probably different representation than the VB string type, though they
effectivly hold the same information. Google for "passing C-strings to
VB" or similar. I know there was a KB article on MSDN showing how to
pass strings from VB to C Dll and back. If I remember correctly, you
have to change your function in the Dll to accept a 'char const*'
instead of 'string'. But you better look that up again.

Other than that, _stdcall should probably be __stdcall (two
underscores) .. not sure if that makes a difference tho.

hth
 
R

RB Smissaert

Thanks for the quick reply.
Not sure I agree I am off topic here though.

I also had an inkling that doing:
#include <string>
and declaring as string in the .cpp was a bit too simple.

Will just find an example of a C++ dll that accepts
string arguments from VB and see how that is done.

RBS
 
J

Jakob Bieling

RB Smissaert said:
Not sure I agree I am off topic here though.

Trust me, you are.

Neither Dlls, nor specific calling conventions are part of the
Standard C++ language. In other words, your code will not compile and
run on any other box unless it is running Windows. So your code is
platform specific, and thus leaving the realms of what is defined by
Standard C++, the topic of this group.
I also had an inkling that doing:
#include <string>
and declaring as string in the .cpp was a bit too simple.

Will just find an example of a C++ dll that accepts
string arguments from VB and see how that is done.

I checked my own code. Change your function like so

double __stdcall Framingham2 (long Age,
long SysBP,
double TotChol,
double HDL,
char const* Sex,
char const* Diabetes,
char const* LVH,
char const* Smoke,
long Years = 10)

And it should work. I know this can be tricky, so maybe there are
other things I have overlooked. In that case, please direct further
questions to comp.os.ms-windows.programmer.win32, as you will also get
better help for this problem there.

regards
 
R

RB Smissaert

For somebody who thinks I am in the wrong group you have done a good
job in helping as you were absolutely right. Changing from string to
char const* made it all work nicely. Will just have to figure out now what
char const* exactlyt means/does and that will be it for now.
Also will have to test if this kind of dll actually is faster than running
this from VBA.
Ideally I think this should go in a .xll add-in but that would definitely be
off-topic.
Will post to the group you suggested next time if I have something similar.
Thanks again.

RBS
 
R

RB Smissaert

Just to say that putting this function in a regular C++ dll doesn't speed it
up that much. I got it about 20% faster.
Will now see if I can put this in an .xll, which I think might
be faster as it directly works with the Excel C API.

RBS
 
R

RB Smissaert

OK, doing this in an .xll add-in is indeed much faster.
It timed it 30 times faster in a moderately complex
math function. This is compared with the VBA function.
Compared with the C++ dll it will be about 25 times faster.

RBS
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,692
Latest member
JenniferTi

Latest Threads

Top