• Welcome to Valhalla Legends Archive.
 

[VC++ and VB6] Bad DLL Calling Convention

Started by Joe[x86], November 25, 2005, 07:22 PM

Previous topic - Next topic

Joe[x86]

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:
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;
   }
}


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

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

EXPORTS
  getDLLVersion
  productName
  MsgBox


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

VB Usage:
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


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.
Quote from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.

shout

#1
If you are calling from VB you need to add

extern "C"

to the fuction.

Or, you could do a

extern "C" {

on the entire file with a } at the end of the file.

And when using Mircosoft VC++ *.*, you can use

__declspec(dllexport)

instead of making a DEF file.

So the method would look like:

extern "C" __declspec(dllexport) char productName(char shortname)


Then if you were using C/C++ to call the project, you would probably use a macro as such:

#ifdef EXPORTS
#define WHATEVER extern "C" __declspec(dllexport)
#else
#define WHATEVER extern "C" __declspec(dllimport)
#endif

Joe[x86]

I've added extern "C" to all my procedures, but it worked before anyhow, so eh?
Quote from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.

Kp

Quote from: Joe on November 25, 2005, 07:22 PMI'm pretty much stuck. Anyone know what to do?

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.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Joe[x86]

Does...

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;
   }
}


look any better?
Quote from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.

Kp

Quote from: Joe on November 25, 2005, 10:49 PMDoes...look any better?

Yes, but it's still wrong.  You're returning useless results, and comparing addresses instead of contents.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Joe[x86]

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?
Quote from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.

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.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Joe[x86]

Thank you.

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;
   }
}


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 from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.

Kp

Quote from: Joe on November 26, 2005, 02:52 PMNow, 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?

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.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Joe[x86]

Quote from: Kp on November 26, 2005, 03:26 PM
Quote from: Joe on November 26, 2005, 02:52 PMNow, 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?

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.

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. =(
Quote from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.

Banana fanna fo fanna


Kp

Quote from: Banana fanna fo fanna on November 26, 2005, 06:18 PM
Shouldn't it be __stdcall?

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.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

MyndFyre

Quote from: Banana fanna fo fanna on November 26, 2005, 06:18 PM
Shouldn't it be __stdcall?

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:

#if EXPORT
#define JBBECORE_API extern "C" __stdcall __declspec(dllexport)
#else
#define JBBECORE_API extern "C" __stdcall __declspec(dllimport)
#endif

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.
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

TheMinistered

Visual Basic uses BSTR for strings.  That is what  you should use as well.