Jump to content
Larry Ullman's Book Forums

Calling A Dll


Recommended Posts

Hi,

 

Can someone please help me with this code which just calls a DLL.

 

The code works perfectly in the Dev-C++ IDE but gives me an error indicating that the LoadLibrary/GetProcessAddress fails when I try to execute it in the MS Visual C++ (2008 express) IDE.

 

// A simple program that uses LoadLibrary and 
// GetProcAddress to access the printString function from HelloWorld_CPP.dll.
// It also calls the function in the DLL passing a string to it. 
 
#include <stdafx.h>
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <comdef.h>	// needed to use LPCSTR

typedef int (__cdecl *MYPROC)(LPCSTR); 

/*
LP == long pointer. Just think pointer or char*

C = const in this case I think they mean the character string is a const, not the pointer being const. 

STR is string (UTF-8 I think (one byte per character))



*/

LPCSTR getInputString(void)
{
	std::string theString;	// define the variable to hold the string
	// std::cin.ignore(100, '\n');	// clear out the input buffer
	std::cout << "Enter the string\n";
	std::getline(std::cin, theString);	// get a whole sentence
	LPCSTR newString = theString.c_str();	// convert the entered string to LPCSTR format
	return newString;
		
}
 
int main(void) 
{ 
    HINSTANCE hinstLib; 
    MYPROC ProcAdd; 
    BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; 
 
    // Get a handle to the DLL module.
	hinstLib = LoadLibrary(TEXT("E:\\Documents\\Visual Studio 2008\\Projects\\DLL_Test\\HelloWorld_CPP.dll"));
	// If the handle is valid, try to get the function address.
    if (hinstLib != NULL) 
		{ 
        ProcAdd = (MYPROC) GetProcAddress(hinstLib, "printString"); 
 
        // If the function address is valid, call the function.
        if (NULL != ProcAdd) 
        {
            fRunTimeLinkSuccess = TRUE;
            // OK, now get the string from the user console
			LPCSTR aString = getInputString();
			(ProcAdd) (aString);
            // (ProcAdd) ("Message sent to the DLL function"); // the string that gets 'sent' to the function in the dll
		}
        
		// Free the DLL module.
        fFreeResult = FreeLibrary(hinstLib); 
    } 

    // If unable to call the DLL function, use an alternative.
    if (! fRunTimeLinkSuccess)
	{
        printf("Unable to call the dll\n"); 
		std::cin.get();
		return 1;
	}
}
The environment is Windows 7, 64 bit.

 

Any assistance will be most appreciated.

 

Best regards, Necuima.

Link to comment
Share on other sites

Hi Larry,

 

It compiles OK but the execution fails with the "Unable to call the dll" message.

 

I have tried single forward slashes in the LoadLibrary statement instead of the double back-slashes but the outcome is the same. In the Dev-C++ version it uses forward slashes but the Visual C++ seems to need the double back-slashes.

 

I have triple-checked and the DLL is definitely there.

 

My admittedly meagre understanding of DLLs is that it does not matter how they were created as long as they function correctly and are called correctly. The DLL called by the code above was developed on my PC in Dev-C++ and simply prints out the string passed to it in a message-box. I did this to try and learn a little about DLLs - how they are created and how they are used. Please correct me if my understanding is not correct, e.g., do I need to compile the DLL in Visual C++ as well?

 

I will keep pursuing this as it's a complete mystery to me!!

 

Again, any advice/suggestions will be most welcome.

 

Cheers from Oz.

Link to comment
Share on other sites

Hi Larry,

 

OK, I set a break point and ran it in debug mode and lo and behold the LoadLibrary statement fails with error "CXX0030: Error: expression cannot be evaluated". It is a bit of a mystery to me why the Dev-C++ executable, which executes exactly the same LoadLibrary statement as the Visual Studio version, works just fine yet the VS one gives this error. I will Google it further.

 

Cheers from Oz.

Link to comment
Share on other sites

Hi again,

 

Just a point of clarity - the error is not occurring in the DLL, it is occurring in the module (code above) which tries to call the DLL.  If I can get past this, then we'll see if the DLL works OK when called by a MS Visual C++ executable rather than the one created by Dev-C++ (which as mentioned, works fine).

 

Cheers, and thanks for the interest.

Link to comment
Share on other sites

Further thought - I suppose that it is possible that the Express version of Visual C++ 2008, which I am trying to use, does not support this??  But I can't find any evidence to support that conjecture!

 

To quote the King of Siam 'Tis a puzzlement" !!

 

Cheers.

Link to comment
Share on other sites

Hi Necuima,

 

The way you call LoadLibrary looks reasonable and certainly works in Visual C++ (2010 Express).

 

Provided it's LoadLibrary that fails, one possible cause might be that the DLL and your executable don’t match in terms of being 32-bit or 64-bit.

A quick way to check that would be to add a line like this in your code:

 

int numBytes = sizeof( void* );

 

then check if the value of numBytes is the same in Visual C++ project and in your Dev-C++ project. Maybe your Dev-C++ project luckily matches the DLL, but the Visual C++ one doesn’t.

 

Another possibility is that one has been built with unicode support and the other one - without. You can check that in the project settings.

If you have the code for it, your best bet would be to rebuild the DLL in Visual C++ and make sure its settings match your app project settings.

 

Also, the error message you get looks like a red herring. Try adding GetLastError right after you call LoadLibrary and see if that gives you more useful information.

 

Good luck,

Radoslava, easynativeextensions.com

Link to comment
Share on other sites

Hi Radoslava,

 

Many thanks for your reply and advice. I am trying to re-build the dll in VC++ 2008 EE but to be honest do not really know what I'm doing!!

 

Here's my code:

#include "stdafx.h"
#define export extern "C" __declspec (dllexport)

// this is the entry point to the dll
BOOL APIENTRY DllMain (HINSTANCE hInst     /* Library instance handle. */ ,
                       DWORD reason        /* Reason this function is being called. */ ,
                       LPVOID reserved     /* Not used. */ )
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        break;

      case DLL_PROCESS_DETACH:
        break;

      case DLL_THREAD_ATTACH:
        break;

      case DLL_THREAD_DETACH:
        break;
    }

    /* Returns TRUE on success, FALSE on failure */
    return TRUE;
}

//Tell the c++ compiler to switch to standard C compiling so exported function names are not mangled

extern "C" {

//passing incoming string to MessageBox
export double printString(LPCWSTR string)
	{
		//Return the string that the calling program passed to this function in a MessageBox.
		return (double) MessageBox(0,string,L"Sample Print String Function", 0);
	}

} //end of C extern
I got this code from another post somewhere - it worked for me once but now I can't even get it to compile in the Dev-C++ IDE!!

 

My lack of knowledge in this area would fill volumes!

 

The compiler error that I'm getting is unresolved external symbol _WinMain@16 referenced in function ___tmainCRTStartup

and .......\VS_2008_DLL\Debug\VS_2008_DLL.exe : fatal error LNK1120: 1 unresolved externals

 

Any help will be most appreciated!

 

Cheers from Oz.

Link to comment
Share on other sites

OK, I finally found a step-by-step guide on creating a DLL and then using it for a Visual C++ IDE. 

 

And it works!!

 

Sorry if I have wasted anyone's time - please ignore the post above!

 

If you're interested the link I found is http://msdn.microsoft.com/en-us/library/ms235636.aspx

 

The example uses implicit linking - I will now try and modify it to use explicit linking!!

 

Cheers from Oz.

Link to comment
Share on other sites

Re the explicit linking call to the dll - I feel (hopefully not being delusional) that I'm close.  At least the LoadLibrary is loading the dll now.

 

But I'm having trouble getting the address of a function in the dll.  I can see that the dll indeed has functions (from the MSDN C++ IDE) but I can't seem to get the function name OK.

 

Here's a code snippet of my GetProcAddress try:

ProcAdd = (MYPROC) GetProcAddress(hinstLib, "MathFuncs::MyMathFuncs::Add"); 
 
// If the function address is valid, call the function.
 
if (NULL != ProcAdd) 
{ ......

I've tried "Add" on its own and various other combinations.

 

Here's the .h file which I understand defines the function to the outside world:

// MathFuncsDll.h
#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport) 
#else
#define MATHFUNCSDLL_API __declspec(dllimport) 
#endif
namespace MathFuncs
{
// This class is exported from the MathFuncsDll.dll
class MyMathFuncs
{
public: 
// Returns a + b
static MATHFUNCSDLL_API double Add(double a, double ; 
// Returns a - b
static MATHFUNCSDLL_API double Subtract(double a, double ; 
// Returns a * b
static MATHFUNCSDLL_API double Multiply(double a, double ; 
// Returns a / b
// Throws const std::invalid_argument& if b is 0
static MATHFUNCSDLL_API double Divide(double a, double ; 
};
}

 

As always, any suggestions will be most welcomed!

 

Cheers from Oz.

 

Link to comment
Share on other sites

Hi again, as far as I can see, I am doing things right but GetLastError() indicates that the procedure name can't be found - the exact error is "GetProcAdsdress failed with error 127: the specified procedure could not be found".  OK, that's clear enough, but when I look at the dll file with DLL Export Viewer, it shows that a procedure with this name is there. The exact name that DLL Export Viewer shows is "public static double __cdecl MathFuncs::MyMathFuncs::Add(double, double)" with ordinal 1.

 

Surely I don't have to key all that in?

 

I have also tried to get the address of the procedure using the ordinal approach and while something is found, it does not seem to be the "Add" function in the dll as 10 + 10 = -SomeHugeNumber!!

 

To use the ordinal approach I followed some advice in StackOverflow:

int ordinal = 1;

and then in the GetProcAddress:

ProcAdd = (MYPROC) GetProcAddress(hinstLib, (LPCSTR) ordinal);

 

Again, any help/ideas will be most appreciated!

 

This is starting to drive me insane!!

Link to comment
Share on other sites

OK, I have managed to prove that the general approach works fine for a different dll file so it must be something to do with this specific dll and the ::s in "MathFuncs::MyMathFuncs::Add".

 

I just don't understand this well enough I guess but at least I've been able to show that the general explicit call approach can work in my environment (Windows 7, 64bit).

 

Thanks for anyone who looked at this and pondered how to help!

 

Cheers from Oz.

Link to comment
Share on other sites

Thanks Larry.

 

My plan is to go back to basics and properly learn C++ (from your book and one which specifically targets the Visual C++ IDE that I am starting to use).

 

If I ever figure it out, with your permission I could post the solution!

 

Best wishes from Oz.

Link to comment
Share on other sites

OK, I bypassed the problem. I still don't know exactly what the problem was but it certainly is to do with the 'funny' function name "MathFuncs::MyMathFuncs::Add".

 

My 'bypass' was to develop another DLL without the class structure of the MSDN example and it works just fine :-)

 

I learned a few things along the way!

 

To create a Windows DLL in Visual C++ there are a couple of pointers to note:

- after all the normal includes, you 'tell the world' that this module is exporting a function for use by others. At first the term 'export' confused me but it seems to just mean that the function will be available to the outside world.

The statement in my case is: #define export extern "C" __declspec (dllexport)

- You can have functions within your DLL that are not visible to the outside world and they are just 'ordinary' C++ functions.

- The function that needs to be made visible to the outside world, i.e. the one that is to be 'exported' needs to have it's name so that it won't get 'mangled' by the C++ compiler. So you include all that function's code within an 'extern' statement - in my case: extern "C" {

then the beginning of the function itself:

export int functionName(LPCSTR fileName, int param1 = 80, int param2 = 1000) ....

... and at the end: } //end of C extern

The module that will call this dll function also needs some 'special' code - note that I am calling the dll explicitly - calling it implicitly is different and an example can be found in the MSDN tutorial mentioned above.

So for the explicit call, after the usual includes I have: typedef int (__cdecl *MYPROC)(LPCSTR, int, int);

As far as I understand, it states that this module will use an explicitly called DLL which will receive an LPCSTR and two ints as inputs and will return an int.

 

I hope that this will be of help to some poor soul who, like me, Googled for literally weeks before finally getting this to work, and there was a lot of trial and error involved.

 

But it's nice to have gained some understanding of DLLs and their use:-)

 

Cheers from Oz.

 

P.S., my environment for this is Windows 7, 64 bit and Visual C++ 2008 Express Edition.

Link to comment
Share on other sites

  • 1 month later...
 Share

×
×
  • Create New...