Valhalla Legends Archive

Programming => General Programming => Topic started by: Joe[x86] on November 25, 2005, 07:22 PM

Title: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 25, 2005, 07:22 PM
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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: shout on November 25, 2005, 09:17 PM
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
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 25, 2005, 10:09 PM
I've added extern "C" to all my procedures, but it worked before anyhow, so eh?
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Kp on November 25, 2005, 10:33 PM
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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 25, 2005, 10:49 PM
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?
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Kp on November 25, 2005, 11:25 PM
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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 25, 2005, 11:44 PM
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?
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Kp on November 26, 2005, 11:26 AM
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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 26, 2005, 02:52 PM
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?
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: 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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 26, 2005, 03:28 PM
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. =(
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Banana fanna fo fanna on November 26, 2005, 06:18 PM
Shouldn't it be __stdcall?
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Kp on November 26, 2005, 06:37 PM
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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: MyndFyre on November 26, 2005, 06:42 PM
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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: TheMinistered on November 26, 2005, 07:05 PM
Visual Basic uses BSTR for strings.  That is what  you should use as well.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Yoni on November 26, 2005, 07:19 PM
Quote from: TheMinistered on November 26, 2005, 07:05 PM
Visual Basic uses BSTR for strings.  That is what  you should use as well.
N and O put together spell NO
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: MyndFyre on November 26, 2005, 09:52 PM
Quote from: TheMinistered on November 26, 2005, 07:05 PM
Visual Basic uses BSTR for strings.  That is what  you should use as well.
BSTR is a COM type, not a C type.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: UserLoser. on November 27, 2005, 12:20 AM
Blerf.cpp:

void __stdcall Bleh(int blah, char *boo)
{
    if(blah > 0)
        strcpy(boo, "bleh");
}


Blerf.def:

Bleh=Bleh


APIDeclares.bas:

Declare Sub Bleh Lib "Blerf.dll" (ByVal Blah As Long, ByVal Boo As Long)


CoolModule.bas:

Sub Meh()
    Dim Eh As String
    Eh = Space(256)
    Call Bleh(1, Eh)
End Sub


Bleh, you get the point
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: TehUser on November 27, 2005, 11:52 AM
Quote from: Joe on November 26, 2005, 02:52 PM
<snip>
   if (ret == "") {
      return product;
   } else {
      return ret;
   }


You're still trying to use == to compare strings.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Kp on November 27, 2005, 12:10 PM
Quote from: TehUser on November 27, 2005, 11:52 AM
Quote from: Joe on November 26, 2005, 02:52 PM
<snip>
   if (ret == "") {
      return product;
   } else {
      return ret;
   }


You're still trying to use == to compare strings.

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.
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 27, 2005, 01:25 PM
Quote from: TehUser on November 27, 2005, 11:52 AM
Quote from: Joe on November 26, 2005, 02:52 PM
<snip>
   if (ret == "") {
      return product;
   } else {
      return ret;
   }


You're still trying to use == to compare strings.

Oops..

Quote from: Kp on November 27, 2005, 12:10 PM
Quote from: TehUser on November 27, 2005, 11:52 AM
Quote from: Joe on November 26, 2005, 02:52 PM
[...]
You're still trying to use == to compare strings.
[...]unless his compiler is totally braindead [...]

Lets asume VC++ is braindead. =p
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: TheMinistered on November 27, 2005, 04:16 PM
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.

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
Title: Re: [VC++ and VB6] Bad DLL Calling Convention
Post by: Joe[x86] on November 27, 2005, 09:30 PM
I'm not using MFC.