Valhalla Legends Forums Archive | General Programming | [VC++ and VB6] Bad DLL Calling Convention

AuthorMessageTime
JoeTheOdd
I'm trying to use ProductName() in a dll, JBBECore.dll (written in VC++), which is called by my bot, JBBE (written in VB6).

VC++ Code:
[tt]char productName(char shortname) {
char ret;
if(shortname == (char[5])"CHAT") { ret = (char)"a chat bot";          }
if(shortname == (char[5])"DSHR") { ret = (char)"Diablo Shareware";    }
if(shortname == (char[5])"DRTL") { ret = (char)"Diablo";              }
if(shortname == (char[5])"JSTR") { ret = (char)"Japanese Starcraft";  }
if(shortname == (char[5])"SSHR") { ret = (char)"StarCraft Shareware"; }
if(shortname == (char[5])"STAR") { ret = (char)"StarCraft";          }
if(shortname == (char[5])"SEXP") { ret = (char)"StarCraft: BW";      }
if(shortname == (char[5])"W2BN") { ret = (char)"WarCraft III: BNE";  }
if(shortname == (char[5])"D2DV") { ret = (char)"Diablo II";          }
if(shortname == (char[5])"D2XP") { ret = (char)"Diablo II: LOD";      }
if(shortname == (char[5])"WAR3") { ret = (char)"WarCraft III";        }
if(shortname == (char[5])"W3XP") { ret = (char)"WarCraft III: TFT";  }
if (ret == NULL) {
return (char)shortname;
} else {
return ret;
}
}[/tt]

VC++ Export:
[tt]; JBBECore.def : Declares the module parameters for the DLL

LIBRARY "JBBECore"
DESCRIPTION "Visual C++ backend of JBBE."

EXPORTS
  getDLLVersion
  productName
  MsgBox[/tt]

VB Declaration:
[tt]Public Declare Function ProductName Lib "JBBECore.dll" Alias "productName" (ByVal Product As String) As String[/tt]

VB Usage:
[tt]Private Sub BNCS_UserJoins(sUsername As String, sStatstring As String, lPing As Long, Product As String)
    Call AddChat(Me.rtbChat, vbYellow, sUsername & " has joined the channel using " & ProductName(Product) & ".")
End Sub[/tt]

I'm pretty much stuck. Anyone know what to do?

EDIT -
For comparison, JBBECore and JBBE are like SphtBotCore and SphtBot, the core being a VC++ DLL, and the bot itself written in VB.
November 26, 2005, 1:22 AM
shout
If you are calling from VB you need to add
[code]
extern "C"
[/code]
to the fuction.

Or, you could do a
[code]
extern "C" {
[/code]
on the entire file with a } at the end of the file.

And when using Mircosoft VC++ *.*, you can use
[code]
__declspec(dllexport)
[/code]
instead of making a DEF file.

So the method would look like:
[code]
extern "C" __declspec(dllexport) char productName(char shortname)
[/code]

Then if you were using C/C++ to call the project, you would probably use a macro as such:
[code]
#ifdef EXPORTS
#define WHATEVER extern "C" __declspec(dllexport)
#else
#define WHATEVER extern "C" __declspec(dllimport)
#endif
[/code]
November 26, 2005, 3:17 AM
JoeTheOdd
I've added [tt]extern "C"[/tt] to all my procedures, but it worked before anyhow, so eh?
November 26, 2005, 4:09 AM
Kp
[quote author=Joe link=topic=13345.msg135337#msg135337 date=1132968172]I'm pretty much stuck. Anyone know what to do?[/quote]

Fixing your C code would be a good start.  I suppose you've managed to beat it into submission with all those typecasts, but I'm really surprised your compiler even built the code you posted.
November 26, 2005, 4:33 AM
JoeTheOdd
Does...

[tt]extern "C" char PASCAL productName(LPCSTR product) {
LPCSTR ret = "";
if(product == "CHAT") { ret = "a chat bot";          }
if(product == "DSHR") { ret = "Diablo Shareware";    }
if(product == "DRTL") { ret = "Diablo";              }
if(product == "JSTR") { ret = "Japanese Starcraft";  }
if(product == "SSHR") { ret = "StarCraft Shareware"; }
if(product == "STAR") { ret = "StarCraft";          }
if(product == "SEXP") { ret = "StarCraft: BW";      }
if(product == "W2BN") { ret = "WarCraft III: BNE";  }
if(product == "D2DV") { ret = "Diablo II";          }
if(product == "D2XP") { ret = "Diablo II: LOD";      }
if(product == "WAR3") { ret = "WarCraft III";        }
if(product == "W3XP") { ret = "WarCraft III: TFT";  }
if (ret == "") {
return (char)product;
} else {
return (char)ret;
}
}[/tt]

look any better?
November 26, 2005, 4:49 AM
Kp
[quote author=Joe link=topic=13345.msg135350#msg135350 date=1132980593]Does...look any better?[/quote]

Yes, but it's still wrong.  You're returning useless results, and comparing addresses instead of contents.
November 26, 2005, 5:25 AM
JoeTheOdd
Well, you can see I have no clue what I'm doing. Could you enlighten me on how to do this correctly? I'm not asking you to spoon feed me, but to give me some instructions or examples that would help me.

I realized I was comparing the pointer to the value itself. I can't get the actual value pointed to, no matter what I do. Could you show me an example of that being done?
November 26, 2005, 5:44 AM
Kp
First rule: don't use a typecast unless you're certain you know better than the compiler.  Too many people just start throwing typecasts in to make warnings/errors go away, but often you're just suppressing the problem.

Make productName return a const char *, and fix the return statements accordingly.  Use strcmp(3) instead of == to test for string equality.
November 26, 2005, 5:26 PM
JoeTheOdd
Thank you.

[code]extern "C" const char * PASCAL productName(const char * product) {
  const char * ret = "";
  if(strcmp(product, "CHAT") == 0) { ret = "a chat bot";          }
  if(strcmp(product, "DSHR") == 0) { ret = "Diablo Shareware";    }
  if(strcmp(product, "DRTL") == 0) { ret = "Diablo";              }
  if(strcmp(product, "JSTR") == 0) { ret = "Japanese Starcraft";  }
  if(strcmp(product, "SSHR") == 0) { ret = "StarCraft Shareware"; }
  if(strcmp(product, "STAR") == 0) { ret = "StarCraft";          }
  if(strcmp(product, "SEXP") == 0) { ret = "StarCraft: BW";      }
  if(strcmp(product, "W2BN") == 0) { ret = "WarCraft III: BNE";  }
  if(strcmp(product, "D2DV") == 0) { ret = "Diablo II";          }
  if(strcmp(product, "D2XP") == 0) { ret = "Diablo II: LOD";      }
  if(strcmp(product, "WAR3") == 0) { ret = "WarCraft III";        }
  if(strcmp(product, "W3XP") == 0) { ret = "WarCraft III: TFT";  }
  if (ret == "") {
      return product;
  } else {
      return ret;
  }
}[/code]

Now, if I'm not mistaken, its returning a pointer to a string, not a string itself. If I just define this as As String will VB handle it correctly?
November 26, 2005, 8:52 PM
Kp
[quote author=Joe link=topic=13345.msg135397#msg135397 date=1133038356]Now, if I'm not mistaken, its returning a pointer to a string, not a string itself. If I just define this as As String will VB handle it correctly?
[/quote]

You are not mistaken.  I try to avoid VB, so I don't know whether VB will handle it correctly.  As a minor performance optimization, you could use else if instead of if, since product can equal at most one of those strings.
November 26, 2005, 9:26 PM
JoeTheOdd
[quote author=Kp link=topic=13345.msg135399#msg135399 date=1133040372]
[quote author=Joe link=topic=13345.msg135397#msg135397 date=1133038356]Now, if I'm not mistaken, its returning a pointer to a string, not a string itself. If I just define this as As String will VB handle it correctly?
[/quote]

You are not mistaken. I try to avoid VB, so I don't know whether VB will handle it correctly. As a minor performance optimization, you could use else if instead of if, since product can equal at most one of those strings.
[/quote]

Ahh, good idea on the else if. I'll do that. :)

A little problem though, VB didn't handle it correctly. I'm trying to copy the pointer to a string now, with very little success. =(
November 26, 2005, 9:28 PM
St0rm.iD
Shouldn't it be __stdcall?
November 27, 2005, 12:18 AM
Kp
[quote author=Banana fanna fo fanna link=topic=13345.msg135416#msg135416 date=1133050736]
Shouldn't it be __stdcall?
[/quote]

That's what I already told him, unless you're trying to pick on my use of only one underscore.  I don't recall encountering a compiler that recognized __stdcall and failed to recognize _stdcall.
November 27, 2005, 12:37 AM
Myndfyr
[quote author=Banana fanna fo fanna link=topic=13345.msg135416#msg135416 date=1133050736]
Shouldn't it be __stdcall?
[/quote]

Yeah, why are you using PASCAL?

Couple thoughts:

LPCSTR instead of const char*.
#define EXP extern "C" __stdcall __declspec(dllexport)

Visual C++ often creates macros for you based on the project name when you create a DLL project.  For example, if your project name was "JBBECore" (which build JBBECore.dll), it'd create a file JBBECore.h.  Among the declarations would be something like:
[code]
#if EXPORT
#define JBBECORE_API extern "C" __stdcall __declspec(dllexport)
#else
#define JBBECORE_API extern "C" __stdcall __declspec(dllimport)
#endif
[/code]
Actually, it will only give you the __declspec parts (not extern "C" __stdcall).  The idea behind this is that you can use the header file for including in other C/++ projects and not define the EXPORT symbol, and the program would know to expect any function marked JBBECORE_API as imported.

You might also consider passing a ByRef String variable as an OUT variable and simply returning the product name that way (instead of through the return statement).

One last note:

A core DLL should not return location-specific strings.  That's the job for localized interface programs.  Your core DLL should always deal with culture-neutral data.  I know you may not care about this right now, but if you decide you want to do something practical with software development one day, it might be a good idea to get into habits of doing this.  Any string the user will see is an interface string, and should be as far out as possible.  This particular usage of a DLL seems a bit superfluous and unnecessary.  Doing this in C won't be any better at all than doing it in VB.
November 27, 2005, 12:42 AM
TheMinistered
Visual Basic uses BSTR for strings.  That is what  you should use as well.
November 27, 2005, 1:05 AM
Yoni
[quote author=TheMinistered link=topic=13345.msg135427#msg135427 date=1133053510]
Visual Basic uses BSTR for strings.  That is what  you should use as well.
[/quote]N and O put together spell NO
November 27, 2005, 1:19 AM
Myndfyr
[quote author=TheMinistered link=topic=13345.msg135427#msg135427 date=1133053510]
Visual Basic uses BSTR for strings.  That is what  you should use as well.
[/quote]
BSTR is a COM type, not a C type.
November 27, 2005, 3:52 AM
UserLoser.
Blerf.cpp:
[code]
void __stdcall Bleh(int blah, char *boo)
{
    if(blah > 0)
        strcpy(boo, "bleh");
}
[/code]

Blerf.def:
[code]
Bleh=Bleh
[/code]

APIDeclares.bas:
[code]
Declare Sub Bleh Lib "Blerf.dll" (ByVal Blah As Long, ByVal Boo As Long)
[/code]

CoolModule.bas:
[code]
Sub Meh()
    Dim Eh As String
    Eh = Space(256)
    Call Bleh(1, Eh)
End Sub
[/code]

Bleh, you get the point
November 27, 2005, 6:20 AM
TehUser
[quote author=Joe link=topic=13345.msg135397#msg135397 date=1133038356]
[code]<snip>
   if (ret == "") {
      return product;
   } else {
      return ret;
   }
[/code]
[/quote]

You're still trying to use == to compare strings.
November 27, 2005, 5:52 PM
Kp
[quote author=TehUser link=topic=13345.msg135485#msg135485 date=1133113976]
[quote author=Joe link=topic=13345.msg135397#msg135397 date=1133038356]
[code]<snip>
  if (ret == "") {
      return product;
  } else {
      return ret;
  }
[/code]
[/quote]

You're still trying to use == to compare strings.
[/quote]

Yes, but unless his compiler is totally braindead, it's ok in this case.  ret was initialized to point at an empty string, and now he's comparing to see whether it points at an empty string.  If the compiler is pooling strings (and it really should!), then the two will have the same address and it'll be OK.
November 27, 2005, 6:10 PM
JoeTheOdd
[quote author=TehUser link=topic=13345.msg135485#msg135485 date=1133113976]
[quote author=Joe link=topic=13345.msg135397#msg135397 date=1133038356]
[code]<snip>
  if (ret == "") {
      return product;
  } else {
      return ret;
  }
[/code]
[/quote]

You're still trying to use == to compare strings.
[/quote]

Oops..

[quote author=Kp link=topic=13345.msg135487#msg135487 date=1133115042]
[quote author=TehUser link=topic=13345.msg135485#msg135485 date=1133113976]
[quote author=Joe link=topic=13345.msg135397#msg135397 date=1133038356]
[...][/quote]You're still trying to use == to compare strings.[/quote][...]unless his compiler is totally braindead [...]
[/quote]

Lets asume VC++ is braindead. =p
November 27, 2005, 7:25 PM
TheMinistered
I was assuming he was using an MFC DLL considering he is just now figuring out how to do it... thus...

[quote]
If we want to pass strings to a DLL as input or to get strings from a DLL as output and we want to do this in the most general way, we are told from Microsoft to use the BSTR type.
[/quote]

The only reason char* works in this case is because it's being passed as ANSI.  However, if microsoft encourages the use of BSTR in this case then why not?

[quote]
Again from the MSDN documentation (in a remote part of it, to be honest !), we read what follows:

1.  Visual Basic always creates a new BSTR containing ANSI characters (not UNICODE ones!) when passing a string to a DLL
2.  Visual Basic always gets a BSTR containing UNICODE characters when getting a string from a DLL
http://www.codeproject.com/dll/BSTR_CString_conversion.asp
[/quote]
November 27, 2005, 10:16 PM
JoeTheOdd
I'm not using MFC.
November 28, 2005, 3:30 AM

Search