When writing an application that uses plugins sometimes it’s necessary to have the plugin call functions from the application. This is fairly easy to do on pretty much any OS but on Windows it requires the plugin to link to the application. Sometimes explicit linking is unreasonable. One situation where you don’t want the plugin linking to the application is when the plugin will be used by multiple applications.
Windows allows for runtime resolution of exported functions
(GetProcAddress)
and this is typically used to access functions in a loaded DLL. What MSDN’s
documentation about GetProcAddress
doesn’t say is this function is not
exclusive to DLLs. GetProcAddress
can also be called from a DLL on an EXE to
get and execute functions from within an EXE. The trick (if you can call it
that) is, have the functions in the EXE you want to access exported.
Following is two sample applications and a sample plugin (DLL). Each application provides the same function prototype with a different implementation. The plugin will call the applications function. The basic design is app1 and app2 calls mod1’s test_func wich calls the applications print_hi function. The implementation of print_hi is different between app1 and app2. mod1 can be used by both app1 and app2 and does not require static linking.
The directory structure for this example is app1/ app2/ mod1/. The sample applications look for the module is the relative directory ../mod1. In real world usage it would be better to assume that the module is in the same directory (or a in a modules subdirectory) as the application.
app1
Makefile.msc
TARGET = app1.exe
CC = cl
AS = ml
LD = link
AR = lib
OBJS =
app1_hi.obj
main.obj
LDFLAGS = /nologo /release /SUBSYSTEM:CONSOLE
DEFINES = /D_CRT_SECURE_NO_DEPRECATE
CFLAGS = /nologo /TP /MD /Os /GF $(DEFINES)
all: $(TARGET)
.c.obj:
$(CC) /c $(CFLAGS) /D "NDEBUG" $<
$(TARGET): $(OBJS)
$(LD) $(LDFLAGS) /out:$@ $(OBJS) kernel32.lib /incremental:no /export:print_hi
clean:
-del *.obj *.exe *.manifest
app1_hi.h
#ifndef APP1_HI_H
#define APP1_HI_H
__declspec(dllexport) void __stdcall print_hi(void);
#endif
app1_hi.c
#include <stdio.h>
__declspec(dllexport) void __stdcall print_hi(void)
{
printf("Hin");
}
main.c
#include <stdio.h>
#include <windows.h>
typedef void (*test_func)(void);
int main(int argc, char **argv)
{
HMODULE h = LoadLibrary("..\mod1\mod1.dll");
if (!h) {
printf("Could not load DLLn");
return 1;
}
test_func tf;
tf = (test_func)GetProcAddress(h, "test_func");
if (!tf) {
printf("Could not locate test_funcn");
return 1;
}
tf();
FreeLibrary(h);
return 0;
}
app2
Makefile.msc
TARGET = app2.exe
CC = cl
AS = ml
LD = link
AR = lib
OBJS =
app2_hi.obj
main.obj
LDFLAGS = /nologo /release /SUBSYSTEM:CONSOLE
DEFINES = /D_CRT_SECURE_NO_DEPRECATE
CFLAGS = /nologo /TP /MD /Os /GF $(DEFINES)
all: $(TARGET)
.c.obj:
$(CC) /c $(CFLAGS) /D "NDEBUG" $<
$(TARGET): $(OBJS)
$(LD) $(LDFLAGS) /out:$@ $(OBJS) kernel32.lib /incremental:no /export:print_hi
clean:
-del *.obj *.exe *.manifest
app2_hi.h
#ifndef APP2_HI_H
#define APP2_HI_H
__declspec(dllexport) void __stdcall print_hi(void);
#endif
app2_hi.c
#include <stdio.h>
__declspec(dllexport) void __stdcall print_hi(void)
{
int a = 12;
int b = 99;
printf("Hello World!n");
printf("Also... n");
printf("a = %in", a);
printf("b = %in", b);
printf("a + b = %i", a + b);
}
main.c
#include <stdio.h>
#include <windows.h>
typedef int (*test_func)();
int main(int argc, char **argv)
{
HMODULE h = LoadLibrary("..\mod1\mod1.dll");
if (!h) {
printf("Could not load DLL: %in", GetLastError());
return 1;
}
test_func tf;
tf = (test_func)GetProcAddress(h, "test_func");
if (!tf) {
printf("Could not locate test_funcn");
return 1;
}
tf();
FreeLibrary(h);
return 0;
}
mod1
Makefile.msc
TARGET = mod1.dll
CC = cl
AS = ml
LD = link
AR = lib
OBJS = main.obj
LDFLAGS = /nologo /release /SUBSYSTEM:CONSOLE
DEFINES = /D_CRT_SECURE_NO_DEPRECATE
CFLAGS = /nologo /TP /MD /Os /GF $(DEFINES)
all: $(TARGET)
.c.obj:
$(CC) /c $(CFLAGS) /D "NDEBUG" $<
$(TARGET): $(OBJS)
$(LD) /DLL $(LDFLAGS) /out:$@ $(OBJS) kernel32.lib /incremental:no /def:exp.def
clean:
-del *.obj *.dll *.manifest
exp.def
LIBRARY mod1.dll
EXPORTS
test_func @1
main.h
#ifndef MAIN_H
#define MAIN_H
__declspec(dllexport) void __stdcall test_func(void);
#endif
main.c
#include <stdio.h>
#include <windows.h>
#include "main.h"
typedef void (*print_hi)(void);
__declspec(dllexport) void __stdcall test_func(void)
{
/* There are two ways we can get the function address. */
#if 0
/* Method 1 */
char filename[MAX_PATH] = {0};
GetModuleFileName(NULL, filename, MAX_PATH-1);
HMODULE h = LoadLibrary(filename);
if (!h) {
printf("Could not load EXEn");
return;;
}
print_hi ph;
ph = (print_hi)GetProcAddress(h, "print_hi");
if (!ph) {
printf("Could not locate print_hin");
return;
}
ph();
FreeLibrary(h);
#endif
/* Method 2 */
print_hi ph = (print_hi)GetProcAddress(GetModuleHandle(NULL), "print_hi");
if (!ph) {
printf("Could not locate print_hin");
return;
}
(*ph)();
}
If you look closely at mod1’s make file you’ll notice that it uses an exports.def file. This is an alternative way to declare exported functions than explicitly putting them in the make file as was done with app1 and app2.
mod1’s main.c also lists two ways to load the functions from the EXE. The first
(commented out) method is more explicit but both methods ultimately use
GetProcAddress
to load the function. The first method gets the name of the
running application (the EXE) wich then calls LoadLibrary
and passes the
returned HMODULE
to GetProcAddress
. The second method uses GetModuleHandle
to
get the HMODULE
for the currently running process.