  ModWorks is very simple Scout that allows finding presence of/unloading/loading
given module and calling a function residing in that module.

--------------------------------------------------------------------------------------------------------------------------------------------------------------------
  All ModWorks functions have ANSI(A)/UNICODE(W) form. UNICODE forms can handle
only unicode characters with values in range 0-255 (codes above are mistranslated).

  Every string passed to ModWorks functions can have (including zero terminator)
max. MAX_PATH characters. If it has more, ModWorks returns ErrorAHException.

  For successful ModWorks execution, the following KERNEL32.dll APIs must be original
(= import entries for those APIs in module with ApiHooks mustn't be altered = module
with ApiHooks mustn't be PE-hooked before it is initialized): GetModuleHandleA,
FreeLibrary, LoadLibraryA, GetProcAddress. If they are PE-hooked, ModWorks may fail
and returns ErrorAHRemote.

  Win9x: When a (large) module(s) is/are about to be loaded into more/all processes,
there must be "enough" (~100 MB) free space available on the drive with paging file
(Win386.swp).

  NT: Don't forget to set appropriate access to module to (un)load. Typically set
Read & Execute for Everyone. It is important for intersession (un)loads.

--------------------------------------------------------------------------------------------------------------------------------------------------------------------
  DWORD __stdcall  IsModuleLoaded(PRCINFO pRCI, LPCTSTR lpszDll, DWORD ProcessId, LONG dwMilliseconds);
  DWORD __stdcall hIsModuleLoaded(PRCINFO pRCI, LPCTSTR lpszDll, HANDLE hProcess, LONG dwMilliseconds);


  RemoteFunction for (h)IsModuleLoaded is implemented as follows:
    RFResult = GetModuleHandle(lpszDll);


  Return codes:
    All AH error codes except ErrorAHSuccess
    or
    RFResult:
      == NULL  - module isn't present in Target.
      != NULL  - module base in Target (module is present in Target).

  9x: TH32 Module32First/Next (faster) instead of IsModuleLoaded should be used.
  2K: PSAPI's EnumModules (probably faster) should be used.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
  DWORD __stdcall  UnloadModule(PRCINFO pRCI, LPCTSTR lpszDll, DWORD ProcessId, LONG dwMilliseconds, DWORD HowManyTimes);
  DWORD __stdcall hUnloadModule(PRCINFO pRCI, LPCTSTR lpszDll, HANDLE hProcess, LONG dwMilliseconds, DWORD HowManyTimes);

    These functions try to unload lpszDll from ProcessId/hProcess during dwMilliseconds
  HowManyTimesx.

  RemoteFunction for (h)UnloadModule is implemented as follows:
    RFResult = GetModuleHandle(lpszDll);
    while((int)(--HowManyTimes) >= 0) {
      FreeLibrary(RFResult);
      RFResult = GetModuleHandle(lpszDll);
    }

  Parameters:
    HowManyTimes - how many times to call FreeLibrary (how many times to unload).

  Return codes:
    All AH error codes except ErrorAHSuccess
    or
    RFResult:
      == NULL  - module isn't present in Target.
      != NULL  - module base in Target (module is present in Target).


  Note:  
    NT only: Statically (at process startup) loaded modules and modules with RefCount > 65534
    can't be unloaded.

--------------------------------------------------------------------------------------------------------------------------------------------------------------------
  DWORD __stdcall  LoadAndCall(PRCINFO pRCI, LPCTSTR lpszDll, DWORD ProcessId, LONG dwMilliseconds, DWORD HowManyTimes, LPCSTR  ApiNameOrOrd, DWORD nArgs, LPVOID pArgs);
  DWORD __stdcall hLoadAndCall(PRCINFO pRCI, LPCTSTR lpszDll, HANDLE hProcess, LONG dwMilliseconds, DWORD HowManyTimes, LPCSTR  ApiNameOrOrd, DWORD nArgs, LPVOID pArgs);


  RemoteFunction for (h)LoadAndCall is implemented as follows:
    if(HowManyTimes == 0)
      RFResult = GetModuleHandle(lpszDll);
    else {
      while(HowManyTimes--) {     
        RFResult = LoadLibrary(lpszDll);
      }
    }
    if(RFResult && (pFunc = GetProcAddress(RFResult, ApiNameOrOrd))) {
      RFResult = pFunc(pArgs[0], ... pArgs[nArgs-1]);
    }


  You've called (h)LAC with non empty ApiNameOrOrd. How to distinguish RFResult when
  a) [lpszDll is loaded but ApiNameOrOrd is not found]
  from
  b) [lpszDll is loaded and ApiNameOrOrd is found] ?

  ApiNameOrOrd should return strict set values different from possible image addresses (i.e. odd numbers)
  or programmmer may check RFResult as follows:
   if(LOWORD(RFResult))
     printf("ApiNameOrOrd returned %u", RFResult);
   else
     printf("lpszDll loaded at %X but ApiNameOrOrd wasn't found probably", RFResult);


  Parameters:
    ApiNameOrOrd - name or ordinal of function to call (assembly language level: function
            can destroy all registers but EBP).
            Supported are:
            1) C family calling conventions (cdecl, stdcall (WINAPI), syscall),
            2) PASCAL family calling conventions (BASIC, FORTRAN), 
            3) fastcall (ECX = pArgs[0], EDX = pArgs[1]),
            4) COM (or C++ member function) calling convention (this in ECX; ECX = pArgs[0]),
            5) Delphi calling convention (PASCAL order, EAX = pArgs[0], EDX = pArgs[1], ECX = pArgs[2]).
    nArgs - can be in <0..20> interval. If it's higher, (h)LoadAndCall returns ErrorAHException.
            Default calling convention is 1). OR nArgs with one of the following constants
            to specify special calling convention:
            LAC_PASCAL, LAC_FASTCALL, LAC_COMCALL, LAC_DELPHI.    
    pArgs - pointer to (or array of) parameters in order it should be passed to function:
            pArgs[3] = {1,2,3}; LoadAndCall(,...,"Function", 3, pArgs);
            calls Function as Function(1,2,3);

  Note:
    lpszDll is loaded, gets DLL_PROCESS_ATTACH followed by (except native processes)
    DLL_THREAD_DETACH notification for the same thread.
    Do not confuse LoadAndCall with (obsolete) Win32 function LoadModule.

--------------------------------------------------------------------------------------------------------------------------------------------------------------------
  See:
    Examples\B-ModWorks.