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

*/