Click here to Skip to main content
15,885,908 members
Please Sign up or sign in to vote.
1.00/5 (2 votes)
See more:
Hi,
I need to call to SEVERAL functions that are inside the EXE from a DLL location. And, I also will need to access some of the variables found inside the EXE from the DLL. Below is the error list, probably not helpful at all, but attached it anyway.
I did the opposite (exe->dll) by using *.def files and dllimport and additional dependencies. And, now I am stuck needing to the other way around.
note: Micros**t Visual Studio 2008 pro, Standard-C.
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _SV_GentityNum が関数 _SV_BotAllocateClient で参照されました。
3>sv_bot.obj : error LNK2001: 外部シンボル "_svs" は未解決です。
3>sv_bot.obj : error LNK2001: 外部シンボル "_sv_maxclients" は未解決です。
3>sv_bot.obj : error LNK2001: 外部シンボル "_botlib_export" は未解決です。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Cvar_Get が関数 _BotDrawDebugPolygons で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _SV_Trace が関数 _BotImport_Trace で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _SV_ClipToEntity が関数 _BotImport_EntityTrace で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _SV_PointContents が関数 _BotImport_PointContents で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _SV_inPVS が関数 _BotImport_inPVS で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _CM_EntityString が関数 _BotImport_BSPEntityData で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _CM_ModelBounds が関数 _BotImport_BSPModelMinsMaxsOrigin で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _CM_InlineModel が関数 _BotImport_BSPModelMinsMaxsOrigin で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Z_TagMalloc が関数 _BotImport_GetMemory で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Z_Free が関数 _BotImport_FreeMemory で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Hunk_Alloc が関数 _BotImport_HunkAlloc で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Hunk_CheckMark が関数 _BotImport_HunkAlloc で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _SV_ExecuteClientCommand が関数 _BotClientCommand で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _VM_Call が関数 _SV_BotFrame で参照されました。
3>sv_bot.obj : error LNK2001: 外部シンボル "_gvm" は未解決です。
2>   ライブラリ ..\..\build\quake3_release\ioquake3.lib とオブジェクト ..\..\build\quake3_release\ioquake3.exp を作成中
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _GetBotLibAPI が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _FS_Seek が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _FS_FCloseFile が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _FS_Write が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _FS_Read2 が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _FS_FOpenFileByMode が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Z_AvailableMemory が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Z_Malloc が関数 _SV_BotInitBotLib で参照されました。
3>sv_bot.obj : error LNK2019: 未解決の外部シンボル _Cvar_VariableIntegerValue が関数 _SV_BotInitBotLib で参照されました。
3>..\..\build\game_release\qagamex86.dll : fatal error LNK1120: 外部参照 28 が未解決です。
2>LINK : warning LNK4098: defaultlib 'LIBCMT' は他のライブラリの使用と競合しています。/NODEFAULTLIB:library を使用してください。
3>ビルドログは "file://c:\Documents and Settings\yasir\デスクトップ\ioquake3dev\ioquake3dev - sandbox\build\game_release\BuildLog.htm" に保存されました。
3>game - エラー 29、警告 0
2>LINK : warning LNK4199: /DELAYLOAD:OleAcc.dll は無視されます。OleAcc.dll にインポートがありません。
2>cm_patch.obj : error LNK2019: 未解決の外部シンボル _BotDrawDebugPolygons が関数 _CM_DrawDebugSurface で参照されました。
2>sv_client.obj : error LNK2019: 未解決の外部シンボル _SV_BotFreeClient が関数 _SV_DropClient で参照されました。
2>sv_game.obj : error LNK2001: 外部シンボル "_SV_BotFreeClient" は未解決です。
2>sv_game.obj : error LNK2019: 未解決の外部シンボル _SV_BotGetConsoleMessage が関数 _SV_GameSystemCalls で参照されました。
2>sv_game.obj : error LNK2019: 未解決の外部シンボル _SV_BotGetSnapshotEntity が関数 _SV_GameSystemCalls で参照されました。
2>sv_game.obj : error LNK2019: 未解決の外部シンボル _SV_BotLibShutdown が関数 _SV_GameSystemCalls で参照されました。
2>sv_game.obj : error LNK2019: 未解決の外部シンボル _SV_BotLibSetup が関数 _SV_GameSystemCalls で参照されました。
2>sv_game.obj : error LNK2019: 未解決の外部シンボル _BotImport_DebugPolygonDelete が関数 _SV_GameSystemCalls で参照されました。
2>sv_game.obj : error LNK2019: 未解決の外部シンボル _BotImport_DebugPolygonCreate が関数 _SV_GameSystemCalls で参照されました。
2>sv_game.obj : error LNK2019: 未解決の外部シンボル _SV_BotAllocateClient が関数 _SV_GameSystemCalls で参照されました。
2>sv_game.obj : error LNK2001: 外部シンボル "_bot_enable" は未解決です。
2>sv_init.obj : error LNK2019: 未解決の外部シンボル _SV_BotFrame が関数 _SV_SpawnServer で参照されました。
2>sv_main.obj : error LNK2001: 外部シンボル "_SV_BotFrame" は未解決です。
2>sv_init.obj : error LNK2019: 未解決の外部シンボル _SV_BotInitBotLib が関数 _SV_Init で参照されました。
2>sv_init.obj : error LNK2019: 未解決の外部シンボル _SV_BotInitCvars が関数 _SV_Init で参照されました。
2>..\..\build\quake3_release\ioquake3.exe : fatal error LNK1120: 外部参照 13 が未解決です。
2>ビルドログは "file://c:\Documents and Settings\yasir\デスクトップ\ioquake3dev\ioquake3dev - sandbox\build\quake3_release\BuildLog.htm" に保存されました。
2>quake3 - エラー 16、警告 2
========== ビルド: 0 正常終了、2 失敗、2 更新不要、1 スキップ ==========
Posted
Comments
#realJSOP 7-Feb-11 7:51am    
Wow - that's readable - NOT.
coffeenet 7-Feb-11 8:18am    
You're a programmer, use your sixth sense to read them :p

You can export functions from an exe in the same way you would from a DLL.

If functions are exported from the exe the compiler will also generate a .lib file that a DLL or another exe can link against.

I generally use __declspec(dllexport) rather than .def files, but both work just as well.

EDIT:
To use the variables from the exe is a little more complicated.
All you need to do is make 2 functions for each variable, 1 to get the value and 1 to set it.
 
Share this answer
 
v2
Comments
coffeenet 7-Feb-11 7:24am    
How about variables? Is there a way to reach a variable on the EXE side from the dll?
coffeenet 7-Feb-11 7:36am    
Btw, do I need to declare those exported functions twice(once with dellexport, once without)?
coffeenet 7-Feb-11 8:27am    
Sorry, but I can't help and ask, since the compiler will fix the exporting stuff for automatically, the exported function can be used inside and outside the exe/dll, then why not assume all functions are exported?
Sergey Alexandrovich Kryukov 7-Feb-11 12:01pm    
Correct - my 5. See also my comments to answer by Richard MacCutchan.
--SA
You cannot link an exe file in the same way you would a dll. If you need to access functions within an exe I think you will need to have them exposed via COM.
 
Share this answer
 
Comments
Andrew Brock 7-Feb-11 5:20am    
This is not true. Functions can be exported from an exe just the same as they can from a DLL.
Richard MacCutchan 7-Feb-11 5:45am    
Thanks, I have never come across this before. Something else for me to study.
Andrew Brock 7-Feb-11 5:47am    
I was quite surprised when I first saw it too.
Sergey Alexandrovich Kryukov 7-Feb-11 12:00pm    
Your statement "You cannot" is not universally true. For example, if EXE is .NET Assembly, if could be linked to other assembly exactly as if it was DLL. More than that, it could be linked even to the unmanaged executable without COM. These are not very well known fact, but people use them. Ask me if you need to know detail: there are good publications here at CodeProject, I just need to find references.
--SA
Richard MacCutchan 7-Feb-11 12:44pm    
Andrew already explained it, and I already acknowledged his comment!
coffeenet says:
How about variables? Is there a way to reach a variable on the EXE side from the dll?


Variables cannot be accessed between a DLL and an EXE as far as I know, you could try
//some_exe_file.cpp
int nMyVar = 0;

//some_dll_file.cpp
extern int nMyVar;


That should work. Otherwise you will need to define a function to get/set the variables
In C++ you could use just 1 function that returns by reference
//some_exe_file.cpp
int nMyVar = 0;
int &GetMyVar() {
	return nMyVar;
}

//some_dll_file.cpp
int &nMyVar = GetMyVar();


Or in C you would need separate get and set functions
//some_exe_file.cpp
int nMyVar = 0;
int GetMyVar() {
	return nMyVar;
}
void SetMyVar(int nNewVal) {
	nMyVar = nNewVal;
}

//some_dll_file.cpp
int nMyVar = GetMyVar();
SetMyVar(2);


coffeenet says:
Btw, do I need to declare those exported functions twice(once with dellexport, once without)?


No. As with DLLs you can use the exported functions inside the module that is exporting them.
There is nothing special to do. The compiler will make any changes needed, and you use them as you would use any other function.
 
Share this answer
 
Comments
coffeenet 7-Feb-11 8:17am    
Man! My first post here and I am having so much help!!!
I am in a dilemma right now, a catch 22, where functions need exported functions from the exe, and functions in the exe need exported functions in the dll. I am gona try and resolve the external errors then come back again. But really thank you guys!!!!!!!!!!!!! :DDDD
coffeenet says:
I am in a dilemma right now, a catch 22, where functions need exported functions from the exe, and functions in the exe need exported functions in the dll.


You have 3 ways to fix this.
You will need to assess which ways are possible, and which will provide the best results in the long term.

1. Move the exported functions that require the DLL out of the exe into another DLL.
This may not solve your issue, depending on what requires what.

2. Move the exported functions from the exe into the DLL that requires them.
This may also not be viable if these functions must be in the exe for whatever reason.

3. Use runtime linking to the DLL.
You don't link to the .lib file produced when compiling the DLL, and you don't directly call any functions in the DLL.
This is done with LoadLibrary (or GetModuleHandle if the library is already loaded which it wont be in this case) and GetProcAddress.

I have written a small class which simplifies this somewhat if you are using C++ you are welcome to use it.
This is a simplified copy of my code. Mine also included error reporting to logs.
template <typename T>
class DynamicFunction {
	public:
		DynamicFunction(LPCWSTR szModule, LPCSTR szProc, bool bFreeWhenDone = true)
			: m_pFunction(NULL), m_bLibLoaded(false), m_bWantUnload(bFreeWhenDone) {
			m_hLibrary = GetModuleHandleW(szModule);
			if (m_hLibrary == NULL) {;
				m_hLibrary = LoadLibraryW(szModule);
				if (m_hLibrary != NULL) {
					m_bLibLoaded = true;
				}
			}
			if (m_hLibrary != NULL) {
				m_pFunction = (T)GetProcAddress(m_hLibrary, szProc);
			}
		}
		DynamicFunction(LPCSTR szModule, LPCSTR szProc, bool bFreeWhenDone = true)
			: m_pFunction(NULL), m_bLibLoaded(false), m_bWantUnload(bFreeWhenDone) {
			m_hLibrary = GetModuleHandleA(szModule);
			if (m_hLibrary == NULL) {;
				m_hLibrary = LoadLibraryA(szModule);
				if (m_hLibrary != NULL) {
					m_bLibLoaded = true;
				}
			}
			if (m_hLibrary != NULL) {
				m_pFunction = (T)GetProcAddress(m_hLibrary, szProc);
			}
		}
		~DynamicFunction() {
			if (m_bLibLoaded) {
				FreeLibrary(m_hLibrary);
			}
		}
		void Unload(bool bForce = false) {
			if (m_bLibLoaded && (m_bWantUnload || bForce)) {
				m_bLibLoaded = false;
				FreeLibrary(m_hLibrary);
				m_hLibrary = NULL;
				m_pFunction = NULL;
			}
		}
		T operator *() {
			return m_pFunction;
		}

	protected:
		HMODULE m_hLibrary;
		T m_pFunction;
		bool m_bLibLoaded;
		bool m_bWantUnload;
};


To use this class:
This sample will dynamically load the function IsWow64Process[^].
This function is only available on Windows XP SP2 and later.
DynamicFunction<BOOL (WINAPI *)(HANDLE, PBOOL)> dfIsWow64Process("Kernel32.dll", "IsWow64Process");
if (*dfIsWow64Process != NULL) {
	BOOL bIsWow64 = FALSE;
	if ((*dfIsWow64Process)(GetCurrentProcess(), &bIsWow64) && bIsWow64) {
		printf("This is a 32 bit process running on a 64 bit computer.\n");
	} else {
		printf("This is either a 32 bit process running on a 32 bit compuer or\n");
		printf("it is a 64 bit process running on a 64 bit compuer.\n");
	}
} else {
	printf("This OS does not have the function IsWow64Process.\n");
}


The general layout of the function is:
DynamicFunction<return_type (call_convention *)(parameter_type, parameter_type, ...)> dfIsWow64Process("dll_name.dll", "function_name");

Most functions you write will just use the default call_convention, so this would not be necessary for those functions.


If you are not using C++, then you have a bit of work to do.
This example is equivelent to the previous sample but will work in C.
typedef BOOL (WINAPI *IsWow64ProcessFunc)(HANDLE, PBOOL);
IsWow64ProcessFunc pIsWow64Process = NULL;
BOOL bIsWow64 = FALSE;
HMODULE hKernel32 = LoadLibrary("Kernel32.dll");
if (hKernel32 == NULL) {
	//error: dll not found
} else {
	pIsWow64Process = (IsWow64ProcessFunc)GetProcAddress(hKernel32, "IsWow64Process");
	if (pIsWow64Process == NULL) {
		//error: function not found
		printf("This OS does not have the function IsWow64Process.\n");
	}
	//do the same for all other functions that you need to load from the DLL
}
if (pIsWow64Process != NULL) {
	if (pIsWow64Process(GetCurrentProcess(), &bIsWow64) && bIsWow64) {
		printf("This is a 32 bit process running on a 64 bit computer.\n");
	} else {
		printf("This is either a 32 bit process running on a 32 bit compuer or\n");
		printf("it is a 64 bit process running on a 64 bit compuer.\n");
	}
}
 
Share this answer
 
Comments
coffeenet 8-Feb-11 1:04am    
This is too much!! Thank you!!

I am going to need sometime to sort out my work and see whether I need/can use the function you kindly written here.
I am at a stage where there are too many problems already, so additional is unlikely. I am leaning towards the second solution:"Move the exported functions from the exe into the DLL that requires them."
I will come back later either to say "thanks again" or "HELP!", let's hope its for the first. :-D
coffeenet 8-Feb-11 2:42am    
Btw, I would like to read about such topics, linkage, libraries...etc Could you kindly recommend one?
Andrew Brock 8-Feb-11 4:55am    
Unfortunately no. I learn most things by typing it in and see what happens.
If it is something that can't be learned that way I either search for it on MSDN or just google what I'm after. I have a search keyword set in my browser that will automatically search MSDN.
http://www.google.com.au/search?hl=en&client=firefox-a&rls=org.mozilla:en-US:official&hs=Rpz&q=GetProcAddress%20site:msdn.microsoft.com&meta=
coffeenet 8-Feb-11 5:12am    
I see. Thanks for the help so far.
I need to maintain where the mechanics reside. So, I have decided to go with your suggested solution number 3. I just finished exporting the functions that are on the dll side.
<pre>
//the preprocessor _ZGAMEDLL is defined on the dll side. the below is inside a shared header between the exe and the dll.
#define _ZGDLLEXPORT __declspec(dllexport)
#define _ZGDLLIMPORT __declspec(dllimport)
#ifdef _ZGAMEDLL //Exporting here
_ZGDLLEXPORT int ZG_Set_bot_enable( int value );//moved from sv_bot.c
#else //Importing here
//functions
_ZGDLLIMPORT void blahblah();
#endif //_ZGAMEDLL
</pre>
I am gona start with the callbacks. I will try to implement your function into mine. I will come back again with updates!

Thanks again mate! You have been really helpful!
coffeenet 8-Feb-11 6:43am    
kk,
As stated above, I have exported all the needed functions of the DLLs.
Now, I want to implement callbacks for the functions that are residing inside the EXE.
I have two questions:
In my case here, if the DLL is prohibited from calling functions inside the EXE, then:
1- How can the DLL trigger callback functions that will eventually call functions inside the EXE.
The piece of code you kindly provided above:
2- Where should it go, DLL or EXE?

I am using Standard-C.
coffeenet says:
1- How can the DLL trigger callback functions that will eventually call functions inside the EXE.
The piece of code you kindly provided above:
2- Where should it go, DLL or EXE?

It seems no matter what way you go you end up using pointers to functions.
This looks daunting, but is rather simple.

I will answer 2 first because it is quick.
The code to dynamically load a function would go in the exe. That would then get a handle to your DLL and load functions from the DLL.

Now for 1.
We use typedef to declare a variable type. This is not required, but simplifies things later.
The syntax of a function type definition is
RETURN_TYPE (CALL_CONVENTION *FunctionTypeName)(PARAMETERS)

CALL_CONVENTION is often left out for functions you write as you just use the default.
RETURN_TYPE includes pointers and/or references (references are only available in C++)
PARAMETERS can be copied directly from the function definition in the .h file or implementation in the .c file.
Parameter names and in/out can be removed to compress the size of the line if you wish.

Lets pass the function strstr into another function as a parameter
//The definition in string.h is:
_Check_return_ _CRTIMP _CONST_RETURN char *  __cdecl strstr(_In_z_ const char * _Str, _In_z_ const char * _SubStr);
//We have
//RETURN_TYPE:  _Check_return_ _CRTIMP _CONST_RETURN
//  "_Check_return_" MUST be removed. It tells you and the compiler that the return value is important. This will cause a compile error if it is not removed. If you are unsure, you can try with this first, if it gives an error then delete it.
//  "_CRTIMP" can be removed. This is used to statically link when compiling a DLL.
//  "_CONST_RETURN" resolves to "const". It must be left in or changed to "const".
//CALL_CONVENTION:  __cdecl
//  This is the default calling convention.
//  It can be removed if you want, and if you haven't changed the default convention.
//PARAMETERS:  _In_z_ const char * _Str, _In_z_ const char * _SubStr
//  "_In_z_" can be removed. It is a guide to tell you that the parameter is an input only.
//  "_Str" and "_SubStr" can be removed. The compiler doesn't care what the variables are called.

//If we copy over everything we can, then we get this (notice the similarity?):
typedef _CRTIMP _CONST_RETURN char *(__cdecl *func_strstr)(_In_z_ const char * _Str, _In_z_ const char * _SubStr);
//As far as the compiler is concerned this is exactly the same as
typedef const char *(__cdecl *func_strstr)(const char *, const char *);
//Or, if you are using the default calling convention, it could be written as
typedef const char *(*func_strstr)(const char *, const char *);


Now that you know how to define a function as a parameter type, we need to use it.
The variable type that is defined as a function (func_strstr in our case) can be used in the exact same way as any other pointer, with 1 addition. It can now be used as a function name to be called.
Keeping with the strstr, you would define a function in the DLL like
__declspec(dllexport) const char *FindSubString(func_strstr pStrStr, const char *szStr, const char *szSubStr) {
	//We just use the function variable as we would use any other function.
	return pStrStr(szStr, szSubStr);
}


And to use this function from the exe you would have
//this definition would be in a .h file somewhere
__declspec(dllimport) const char *FindSubString(func_strstr pStrStr, const char *szStr, const char *szSubStr);

char *szStr = "hello world";
char *szSubStr = "lo";
char *szLocation = FindSubString(&strstr, szStr, szSubStr);


In the above we have FindSubString(&strstr, szStr, szSubStr); which is where the interesting bit happens.
The &strstr is passing a pointer to the function strstr into the function FindSubString.
The ampersand symbol & is optional. I prefer to use it as you are passing in a pointer to the function.

If you have understood everything so far you may have realised that this example is completely pointless as it performs the exact same operation as
char *szStr = "hello world";
char *szSubStr = "lo";
char *szLocation = strstr(szStr, szSubStr);

however the string comparison is done in the context of the DLL.

As for "triggering" a callback, it is up to you when it is triggered. Just pass the function pointer for the function from the exe into the function that is in the DLL and call it as I have shown.

The method of passing function pointers around can also be used for passing a function back from the DLL to the exe as well. The exact same principal applies, however you will probably be passing the function pointer back as the return value of a function.
 
Share this answer
 
Comments
coffeenet 9-Feb-11 5:03am    
:OI had to have someone to explain your explanation for me :D
I never expected passing function pointers was so "OK" that I tried to read deeper.
ANYWAYS, THANK YOU!!!!!!!!!!!!!!!!!!!!!

I will try and implement it now, and see what happens!!
coffeenet 9-Feb-11 5:05am    
I almost forgot, the above func_strstr is exclusive for strstr, right? If so, it doesn't seem a very useful practice for me, since I have at least 11 different functions to be called from the EXE that will need callbacks.
Andrew Brock 9-Feb-11 5:08am    
func_strstr is the name I chose for the variable type.
You can use any name that you wish, as long as it doesn't already exist anywhere.
It cannot have the same name as any other variable (in that scope), function, class/struct name or data type.
coffeenet 9-Feb-11 5:12am    
that was quick :D
I meant, that if I have several functions that I want to have callbacks for, each one will need a differen typedef for the function pointer, right?

Here is my code. I haven't implemented it yet, but could you kindly comment on it:
<pre>
//The function we want to call from the DLL side, this is on the EXE side
void *Hunk_Alloc( int size, ha_pref preference );

//As far as the compiler is concerned this is exactly the same as
typedef const char *(__cdecl *func_zgCBHunk_Alloc)(int, ha_pref );

//DLL SIDE
__declspec(dllexport) *zgCBHunk_Alloc(func_zgCBHunk_Alloc pzgCBHunk_Alloc, int size, ha_pref preference) {
//We just use the function variable as we would use any other function.
return pzgCBHunk_Alloc(size, preference);
}

//H/EXE SIDE
__declspec(dllimport) *zgCBHunk_Alloc(func_zgCBHunk_Alloc pzgCBHunk_Alloc, int size, ha_pref preference);
</pre>
Andrew Brock 9-Feb-11 5:14am    
Sorry, yes that is correct.
Each function typedef must have a unique name.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900