Despite an enormous amount of information available on runtime dynamic linking of DLLs, I cannot get things to work using Win 7 / VS 2010. While there are many pitfalls, my current problem is how to find the 'decorated' names. Here is a snippet of a header file for my dll (lap.dll):
Code:
#ifdef LAP_EXPORTS
#define LAP_API __declspec(dllexport)
#else
#define LAP_API __declspec(dllimport)
#endif
#include "Parser.h"
// This class is exported from the LAP.dll
class LAP_API Clap {
public:
Clap(void);
~Clap(void);
// Attributes
protected:
char * m_pList;
double ** m_fMatrix;
CParser * m_pParser;
// Operations
public:
void PutMatrix(double ** pMatrix);
double ** GetMatrix();
void DumpMatrix(double ** pMatrix);
double **CreateMatrix(int nRows, int nCols);
void FreeMatrix(double ** pMatrix);
double ** Parse(char * expr);
int GetOpMode();
int SaveVariableMap(char * sfile);
int LoadVariableMap(char * sfile);
char * GetKeyList();
char * GetVarList();
char * GetFunList();
void AddVariable(TNT::Matrix<double> Mc, char * svarnam);
int EraseAllVariables();
int RunScript(char * sfile);
int ProcessScript(char* sscript);
};
Both lap.dll and the test app compile, but I cannot figure out how to define the correct function prototypes in the test file.
Here's a fragment of the test file. The handle comes back as valid, but I cannot get the proc address correctly:
Code:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
typedef double ** PARSEPROC(LPSTR);
int _tmain(int argc, _TCHAR* argv[])
{
cout << "Normal Beginning" << endl << endl;
HINSTANCE hinstLib;
PARSEPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(TEXT("lap.dll"));
if (hinstLib != NULL)
{
printf("\nHandle is valid\n");
ProcAdd = (PARSEPROC) GetProcAddress(hinstLib, "Parse");
// If the function address is valid, call the function.
if (NULL != ProcAdd)
{
printf("\nProcAdd is not NULL\n\n");
fRunTimeLinkSuccess = TRUE;
}
// Free the DLL module.
fFreeResult = FreeLibrary(hinstLib);
}
// If unable to call the DLL function, use an alternative.
if (! fRunTimeLinkSuccess)
//printf("Message printed from executable\n");
printf("Unable to call the DLL function\n");
printf("\n\nNormal Termination\n\n");
getchar();
return 0;
}
All my efforts have failed to capture the procedure address. In fact, the way the test app is written here, it will not compile as it will not allow assignment (or casting) of the GetProcAddress, even though GetProcAddress(hinstLib, "Parse"); by itself will compile and run. I am uncertain whether this problem is due to name decoration, test app syntax, def file syntax, or some other problem.
There used to be a way to set the compiler to generate docorated names file when compiling a dll, but I can not find such a tool in VS 2010. The DumpBin.exe tool needed to have the path to mspdb100.dll set. When run against lap.dll, it DumpBin produces the following :
Any help greatly appreciated. But I've looked at a number of tutorials and most are either outdated or deal only with C declared function. It is worth noting that lap.dll has performed flawlessly when used as a staically bound dll.
Last edited by Mike Pliam; July 30th, 2011 at 10:02 PM.
most are either outdated or deal only with C declared function
I deal with dlls since 1996. And I'm still not aware of a way how to wire up a pure C++ class from dll dynamically, what actually you're trying to do.
Well, the available options are:
wrap the class into COM class
expose a needed subset of class functions as interface
In other words, you have to re-design the dll to let it be linked at runtime. Or make a proxy dll with plain C exports which links the dll statically (the way most of plugins do).
Last edited by Igor Vartanov; July 30th, 2011 at 10:35 PM.
The handle comes back as valid, but I cannot get the proc address correctly:
Instead of going through trial and error with calling GetProcAddress, you should know that this function works in a very simple manner. In addition you can use the GUI tool Dependency Walker to get the list of exported names, and those exported names are the only valid ones that GetProcAddress will work with.
As to GetProcAddress, whatever name you state in the second parameter must match exactly with the name that is exported from the DLL. This includes casing, any special characters such as @, ?, etc. So if the exported function is "SomeClass@Parse@@int??" or some other weird name, then that is the name you must use for GetProcAddress to work. GetProcAddress doesn't know anything about name mangling, non-mangled names, def files, nothing. All it knows about is that final name that shows up in the export table of the DLL, and that is the name you must use to return a valid address.
Regards,
Paul McKenzie
Last edited by Paul McKenzie; July 30th, 2011 at 11:00 PM.
Mike, in addition to what Paul just said. I believe you don't see a difference between global C++ function and C++ class member function. Suppose you obtained the function address, what you could do with that? Calling it directly gonna crash your app for 100%. To be called properly, the Clap object should be created first. Which means Clap constructor should be resolved and called properly. And so on and so forth...
I want to give an application interface away for free, then charge for modular updates. I think the runtime dynamic linking dll is the way to go here. 1) Your client only needs to download and install a new dll and not the whole program interface 2) if the dll called by the client is not available (he didnt purchase it yet), the program can handle it and it will still run(unlike what happens if a static dll is missing). To accomplish this, the main app interface needs to be able to call the new dll function(s). Since there are only a few functions in the dll that serve as a gateways to the dll library, this shouldnt be much of a problem. I think that this is the method used by many programs to provide updates, add-ons, and plug-ins.
Paul, I downloaded and employed DependencyWalker, dragging lap.dll onto it's main window, and was quickly able to obtain a complete list of the decorated function names. THANKS! With that, I am able to obtain a Proc Address that is presumably valid. Now, how to use that address to execute the desired function in the test app?
Code:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
typedef double ** PARSEPROC(LPSTR);
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hinstLib;
FARPROC ParseProcAddr;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(TEXT("lap.dll"));
if (hinstLib != NULL)
{
printf("\nHandle is valid\n");
ParseProcAddr = GetProcAddress(hinstLib, "?Parse@Clap@@QAEPAPANPAD@Z"); // Tochiba
if (ParseProcAddr == NULL) { printf("ParseProcAddr is NULL\n"); }
// If the function address is valid, call the function.
if (NULL != ParseProcAddr)
{
printf("\nParseProcAddr is not NULL\n\n");
fRunTimeLinkSuccess = TRUE;
double ** pMatrix;
//pMatrix = (double **)(ParseProcAddr)("x = 3");
}
// 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 function\n");
return 0;
}
Output:
Handle is valid
ParseProcAddr is not NULL
So the problem now becomes, given a function prototype definition, double ** PARSEPROC(LPSTR), and given a presumably valid dll procedure address, FARPROC ParseProcAddr, how do I use these to execute the procedure and retrieve the double ** ? That which I have commented out above will not compile for reasons of both assignment and casting errors.
Why not use COM where the interface is compiler and platform independent?
Trying to expose C++ classes is a pain due to name decoration. And the real problem is the decoration could change between compiler versions or even compiler update.
For this to work reliably, you'll need to build the app and the new plugins with the same compiler. I would avoid that situation if I were you.
Paul, I downloaded and employed DependencyWalker, dragging lap.dll onto it's main window, and was quickly able to obtain a complete list of the decorated function names. THANKS!
However, I agree with Arjay. You do not want to get into the business of using mangled names, as that can change very easily (and is not compiler independent).
Either you use COM, or you use non-mangled names and create 'C' exported functions (similar to how most professional non-COM DLL's are done).
For the latter, you probably have to rethink your design in terms of trying to export a class. Instead you need to create a "Matrix API" that creates, destroys, and manipulates a Matrix, without having the client to even know there is a class internal to the DLL that is working behind the scenes.
I want to give an application interface away for free, then charge for modular updates.
Then your approach of native exporting C++ class is wrong. What you need is "expose a needed subset of class functions as interface". Of course additionally you'll need some API for object creation/destruction, like Paul already said. The technique is really mature and proven to be safe and effective. With certain effort your dll becomes even cross language compatible and this way installable in third party apps, letting you reach clients beyond C++. As I mentioned, most of plugins go this way.
Last edited by Igor Vartanov; August 1st, 2011 at 02:36 AM.
OK. I get the point. I have abandoned the idea of exporting class members and have began to experiment with writing simple C language DLL exports. I have attached a small demo that shows how to use runtime dynamic dll linking to export functions that return integers, doubles, and double pointers. But the pointers present a problem that Paul helped me to solve some time ago. Namely, once memory is allocated in the dll space, how does one deallocate that memory. With static linking and using class member exports, one could take advantage of the class destructor to free allocated memory and thus avoid memory leaks. But I cannot figure out how to do memory cleanup using only C. Note that the attached demo works but with considerable memory leak.
But the pointers present a problem that Paul helped me to solve some time ago. Namely, once memory is allocated in the dll space, how does one deallocate that memory
There should be another exported function that the client calls to clean up the memory and do any other housekeeping to remove the Matrix.
In addtion, the client shouldn't know the internals of how to clean up or properly destroy the Matrix. Today it could be delete[], tomorrow it could be GlobalFree(), next year it could be some other function internally -- the client shouldn't need to know this (or care).
Regards,
Paul McKenzie
Last edited by Paul McKenzie; August 1st, 2011 at 12:07 PM.
Then if I execute the vector producing function and the cleanup using runtime dynamic linking in the test app,
Code:
typedef double *(*FVEC)();
typedef void (*CLEANUP)();
int _tmain(int argc, _TCHAR* argv[])
{
int iOrg = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
_CrtSetDbgFlag(iOrg | _CRTDBG_LEAK_CHECK_DF);
cout << "Normal Beginning" << endl << endl;
HINSTANCE hinstLib;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
FVEC _MYVEC = NULL;
CLEANUP _MYCLEANUP = NULL;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(TEXT("my.dll"));
if (hinstLib != NULL)
{
_MYVEC = (FVEC)GetProcAddress(hinstLib, "fvec");
_MYCLEANUP = (CLEANUP)GetProcAddress(hinstLib, "cleanup");
// If the function address is valid, call the function.
if (NULL != _MYVEC)
{
printf("\n_MYVEC is not NULL\n\n");
fRunTimeLinkSuccess = TRUE;
printf("%0.0f\n", _MYVEC()[0]);
for(int i=1; i <= _MYVEC()[0]; i++)
{
printf("%0.3f ", _MYVEC()[i]);
}
printf("\n");
}
if (NULL != _MYCLEANUP)
{
printf("\n_MYCLEANUP is not NULL\n\n");
fRunTimeLinkSuccess = TRUE;
_MYCLEANUP();
}
// 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 function\n");
return 0;
}
the test application compiles and runs just fine, and I can see that the 'cleanup ran ok', yet there remains a memory leak corresponding to the memory allocated for the vector.
Where does the "decorated stuff" start and end? It's a lot easier to use a tool that will tell you *exactly* what the decorated name is (instead of making guesses).
If I counted correctly, you're calling _MYVEC() (and in turn fvec() and CreateVector() inside the DLL) a total of 12 times. Each of these calls except the first one not only allocates a new vector, it also overwrites the pointer to the previous one. When you finally call _MYCLEANUP(), this merely deallocates the last vector you allocated. Hence there are 11 vector memory blocks leaking.
I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.
This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.