new.c
/*
$RCSfile: new.c,v $
$Revision: 1.4 $
$Date: 1999-07-24 17:47:31-07 $
$Author: doomer $
this is an example that shows how to change the behavior
of an old dll you no longer have the source code to. the
technique involves dynamically loading the old dll from
this one. for functions in the old dll whose behavior you
wish to change, you can either implement them entirely in
this dll, or call the old function, then change the result.
we show how to accomplish the latter by implementing a
function called func1() that is also defined in the old dll.
for old functions you do not wish to change, you can implement
a function of the same name in the new dll. the new
implementation calls the old function. doing this minimizes
the number of changes you will need to make to the vee
program.
if you would rather not define all of the functions in the
new dll, you may have the vee program call the old dll to
invoke the functions which need not change. this results in
less coding in the new dll but more changes in the vee
program.
*/
/*
windows type definitions and function prototypes.
*/
#include <windows.h>
/*
manifest constants that indicate the cause of
an error in our dll.
*/
#define ERROR_BASE -1000
#define E_LOAD_LIB ERROR_BASE
#define E_FIND_FUNC ERROR_BASE -1
/*
it may not be possible to call a function in the old
dll if the old dll has not been loaded or we cannot find
the function. in this case, it is not possible to
definitively indicate the error condition with a simple
return value. the error return value may be a valid
result for the function we wish to call. instead, we
throw a vee error, which can be trapped with an error
pin on the call function object. the function prototype
is here, because i cannot find it in any of the header
files in the vee installation directory.
*/
void veeRaiseError(long error_code, const char* error_string);
/*
define new data types that are pointers to functions.
the first is a pointer to a function that returns a long
and takes a void argument. the second is a pointer to a
function that returns a long and takes a long as an argument.
a variable of this type is used to let us call a function in
a library we dynamically load.
*/
typedef long (*p_lv_fn)(void);
typedef long (*p_ll_fn)(long);
/*
when you dynamically load a dll, the system identifies it through
the use of a handle. h_old_dll is the handle to our old dll.
*/
static HINSTANCE h_old_dll;
/*
dll entry point. we do nothing here. you might think that
this would be a good place to attach to and detach from
the dll whose functions we want to call. but doing so will
lead to disaster.
*/
BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, LPVOID reserved){
return TRUE;
}
/*
when a forwarding function tries to call its old equivalent,
it (the forwarding function) checks to see whether the dll
has been loaded or not. if not, the forwarding function
will call this function, which simply loads a dll and updates
the value of the global variable that holds the dll handle.
*/
void load_old_dll(void){
h_old_dll = LoadLibrary("old.dll");
}
/*
this is a function defined in our old dll. we will
call it, then fiddle with the result it returns.
this lets the new dll change the functionality of the
old dll. alternatively, we could have implemented the
new behavior in its entirety right here.
*/
long func1(void){
static p_lv_fn old_func1;
long old_result = 0;
/*
if the old dll has not been successfully loaded or
we do not have a valid pointer to our old function,
we throw an error. a return value might not be enough
to definitively indicate an error, because the error
return value might be a valid result from this function.
*/
if(!h_old_dll){
load_old_dll();
if(!h_old_dll){
veeRaiseError(E_LOAD_LIB,
"Could not dynamically load old.dll");
}
}
if(!old_func1){
old_func1 = (p_lv_fn)GetProcAddress(h_old_dll, "func1");
if(!old_func1){
/*
note that we do not release the DLL if we
fail to find the function we want. since
other functions depend on the dll, releasing
it would require other forwarding functions to
rebind it before calling old functions. this
could cause unnecessary overhead.
*/
veeRaiseError(E_FIND_FUNC,
"Could not locate func1");
}
}
old_result = old_func1();
++old_result;
return old_result;
}
/*
this is a function whose behavior we need not change. we
just call the function in our old dll and return its results.
*/
long func2(long arg){
static p_ll_fn old_func2;
if(!h_old_dll){
load_old_dll();
if(!h_old_dll){
veeRaiseError(E_LOAD_LIB,
"Could not dynamically load old.dll");
}
}
if(!old_func2){
old_func2 = (p_ll_fn)GetProcAddress(h_old_dll, "func2");
if(!old_func2){
veeRaiseError(E_FIND_FUNC,
"Could not locate func2");
}
}
return old_func2(arg);
}
/*
$Log: new.c,v $
Revision 1.4 1999-07-24 17:47:31-07 doomer
decided to change the way we decide
to load the dll and find its functions.
Revision 1.3 1999-07-24 16:46:08-07 doomer
removed apostrophes from comments
Revision 1.2 1999-07-24 16:45:13-07 doomer
comments now resemble intelligible human language
Revision 1.1 1999-06-26 23:49:24-07 doomer
Initial revision
*/