| 1 | /* $Id: windlllx.cpp,v 1.29 2004-01-15 10:39:07 sandervl Exp $ */
|
|---|
| 2 |
|
|---|
| 3 | /*
|
|---|
| 4 | * Win32 LX Dll class (compiled in OS/2 using Odin32 api)
|
|---|
| 5 | *
|
|---|
| 6 | * Copyright 1999-2000 Sander van Leeuwen (sandervl@xs4all.nl)
|
|---|
| 7 | * Copyright 2003 Innotek Systemberatung GmbH (sandervl@innotek.de)
|
|---|
| 8 | *
|
|---|
| 9 | * TODO: Unloading of dlls probably needs to be fixed due to OS/2 bug
|
|---|
| 10 | * (wrong unload order of dlls)
|
|---|
| 11 | *
|
|---|
| 12 | * Project Odin Software License can be found in LICENSE.TXT
|
|---|
| 13 | *
|
|---|
| 14 | */
|
|---|
| 15 | #define INCL_DOSFILEMGR /* File Manager values */
|
|---|
| 16 | #define INCL_DOSERRORS /* DOS Error values */
|
|---|
| 17 | #define INCL_DOSPROCESS /* DOS Process values */
|
|---|
| 18 | #define INCL_DOSMODULEMGR
|
|---|
| 19 | #define INCL_DOSMISC /* DOS Miscellanous values */
|
|---|
| 20 | #define INCL_WIN
|
|---|
| 21 | #include <os2wrap.h> //Odin32 OS/2 api wrappers
|
|---|
| 22 | #include <os2newapi.h>
|
|---|
| 23 | #include <stdio.h>
|
|---|
| 24 | #include <string.h>
|
|---|
| 25 | #include <stdlib.h>
|
|---|
| 26 | #include <iostream.h>
|
|---|
| 27 | #include <fstream.h>
|
|---|
| 28 | #include <misc.h>
|
|---|
| 29 | #include <win32type.h>
|
|---|
| 30 | #include "windllbase.h"
|
|---|
| 31 | #include "windlllx.h"
|
|---|
| 32 | #include "winexepe2lx.h"
|
|---|
| 33 | #include "winexepeldr.h"
|
|---|
| 34 | #include <odinlx.h>
|
|---|
| 35 | #include <wprocess.h>
|
|---|
| 36 | #include "oslibmisc.h"
|
|---|
| 37 | #include <custombuild.h>
|
|---|
| 38 |
|
|---|
| 39 | #include <exe386.h>
|
|---|
| 40 |
|
|---|
| 41 | #define DBG_LOCALLOG DBG_windlllx
|
|---|
| 42 | #include "dbglocal.h"
|
|---|
| 43 |
|
|---|
| 44 | /*******************************************************************************
|
|---|
| 45 | * Global Variables *
|
|---|
| 46 | *******************************************************************************/
|
|---|
| 47 | char *lpszCustomDllName = NULL;
|
|---|
| 48 | PIMAGE_FILE_HEADER lpCustomDllPEHdr = NULL;
|
|---|
| 49 |
|
|---|
| 50 | /**
|
|---|
| 51 | * FreeLibrary callback for LX Dlls, it's call only when the library is actually
|
|---|
| 52 | * being unloaded.
|
|---|
| 53 | * Maintained by ODIN_SetLxDllUnLoadCallback().
|
|---|
| 54 | */
|
|---|
| 55 | PFNLXDLLUNLOAD pfnLxDllUnLoadCallback = NULL;
|
|---|
| 56 |
|
|---|
| 57 | //******************************************************************************
|
|---|
| 58 | //******************************************************************************
|
|---|
| 59 | void WIN32API SetCustomBuildName(char *lpszName, PIMAGE_FILE_HEADER pfh)
|
|---|
| 60 | {
|
|---|
| 61 | lpszCustomDllName = lpszName;
|
|---|
| 62 | lpCustomDllPEHdr = pfh;
|
|---|
| 63 | }
|
|---|
| 64 | //******************************************************************************
|
|---|
| 65 | //Create LX Dll object and send process attach message
|
|---|
| 66 | //System dlls set EntryPoint to 0
|
|---|
| 67 | //Parameters:
|
|---|
| 68 | // HINSTANCE hInstance - OS/2 module handle
|
|---|
| 69 | // WIN32DLLENTRY EntryPoint - Win32 dll entrypoint address
|
|---|
| 70 | // PVOID pResData - pointer to win32 resource data
|
|---|
| 71 | // DWORD MajorImageVersion - major image/os version (for fake win32 header)
|
|---|
| 72 | // DWORD MinorImageVersion - minor image/os version (for fake win32 header)
|
|---|
| 73 | // DWORD Subsystem - subsystem type (for fake win32 header)
|
|---|
| 74 | // (IMAGE_SUBSYSTEM_WINDOWS_CUI/IMAGE_SUBSYSTEM_WINDOWS_GUI)
|
|---|
| 75 | //
|
|---|
| 76 | //Returns: Odin32 module handle
|
|---|
| 77 | //******************************************************************************
|
|---|
| 78 | DWORD WIN32API RegisterLxDll(HINSTANCE hInstance, WIN32DLLENTRY EntryPoint,
|
|---|
| 79 | PVOID pResData,
|
|---|
| 80 | DWORD MajorImageVersion,
|
|---|
| 81 | DWORD MinorImageVersion,
|
|---|
| 82 | DWORD Subsystem)
|
|---|
| 83 | {
|
|---|
| 84 | APIRET rc;
|
|---|
| 85 | Win32LxDll *windll;
|
|---|
| 86 | Win32DllBase *windlldep;
|
|---|
| 87 | char szFileName[CCHMAXPATH], szErrName[CCHMAXPATH];
|
|---|
| 88 |
|
|---|
| 89 | if(!lpszCustomDllName) {
|
|---|
| 90 | if(OSLibGetDllName(hInstance, szFileName, sizeof(szFileName)) == FALSE) {
|
|---|
| 91 | dprintf(("ERROR: RegisterLxDll: OSLibGetDllName %x failed!!", hInstance));
|
|---|
| 92 | return 0;
|
|---|
| 93 | }
|
|---|
| 94 | dprintf(("RegisterLxDll %x %s", hInstance, szFileName));
|
|---|
| 95 | //Make sure DosLoadModule is called at least once for a dll (to make sure
|
|---|
| 96 | //OS/2 doesn't unload the dll when it's still needed)
|
|---|
| 97 | rc = DosLoadModule(szErrName, sizeof(szErrName), szFileName, &hInstance);
|
|---|
| 98 | if(rc != 0) {
|
|---|
| 99 | dprintf(("ERROR: RegisterLxDll: DosLoadModule %s failed (rc=%d)!!", szFileName, rc));
|
|---|
| 100 | return 0;
|
|---|
| 101 | }
|
|---|
| 102 | }
|
|---|
| 103 | else {
|
|---|
| 104 | //make sure this dll hasn't already been loaded
|
|---|
| 105 | if(Win32DllBase::findModule(lpszCustomDllName) != NULL) {
|
|---|
| 106 | dprintf(("ERROR: RegisterLxDll: module %s already loaded!!", lpszCustomDllName));
|
|---|
| 107 | DebugInt3();
|
|---|
| 108 | return 0;
|
|---|
| 109 | }
|
|---|
| 110 | }
|
|---|
| 111 | windll = new Win32LxDll(hInstance, EntryPoint, pResData, MajorImageVersion,
|
|---|
| 112 | MinorImageVersion, Subsystem);
|
|---|
| 113 | if(windll == NULL) {
|
|---|
| 114 | dprintf(("RegisterLxDll: windll == NULL!!!"));
|
|---|
| 115 | return 0;
|
|---|
| 116 | }
|
|---|
| 117 | //clear name override in case dll init loads another dll
|
|---|
| 118 | lpszCustomDllName = NULL;
|
|---|
| 119 | lpCustomDllPEHdr = NULL;
|
|---|
| 120 |
|
|---|
| 121 | if(!fPeLoader) {
|
|---|
| 122 | windll->AddRef();
|
|---|
| 123 |
|
|---|
| 124 | if(windll->attachProcess() == 0)
|
|---|
| 125 | return 0;
|
|---|
| 126 |
|
|---|
| 127 | return windll->getInstanceHandle();
|
|---|
| 128 | }
|
|---|
| 129 | IMAGE_DOS_HEADER doshdr;
|
|---|
| 130 | struct e32_exe lxhdr;
|
|---|
| 131 | ULONG offset;
|
|---|
| 132 | char modulename[CCHMAXPATH];
|
|---|
| 133 | char modsize;
|
|---|
| 134 | int i;
|
|---|
| 135 |
|
|---|
| 136 | //SvL: This code reads the import name table of the dll to get the dependencies
|
|---|
| 137 | // on other dlls.
|
|---|
| 138 | //DosQueryHeaderInfo is an undocumented api, but works very well.
|
|---|
| 139 | //(no need to save FS here as we'll return to OS/2 immediately)
|
|---|
| 140 | rc = DosQueryHeaderInfo(hInstance, 0, &doshdr, sizeof(IMAGE_DOS_HEADER), QHINF_READFILE);
|
|---|
| 141 | if(rc) {
|
|---|
| 142 | goto hdrerror;
|
|---|
| 143 | }
|
|---|
| 144 | rc = DosQueryHeaderInfo(hInstance, doshdr.e_lfanew, &lxhdr, sizeof(e32_exe), QHINF_READFILE);
|
|---|
| 145 | if(rc) {
|
|---|
| 146 | goto hdrerror;
|
|---|
| 147 | }
|
|---|
| 148 | offset = doshdr.e_lfanew + lxhdr.e32_impmod;
|
|---|
| 149 | for(i=0;i<lxhdr.e32_impmodcnt;i++) {
|
|---|
| 150 | rc = DosQueryHeaderInfo(hInstance, offset, &modsize, 1, QHINF_READFILE);
|
|---|
| 151 | if(rc) {
|
|---|
| 152 | goto hdrerror;
|
|---|
| 153 | }
|
|---|
| 154 | rc = DosQueryHeaderInfo(hInstance, offset+1, &modulename, min(modsize, sizeof(modulename)), QHINF_READFILE);
|
|---|
| 155 | if(rc) {
|
|---|
| 156 | goto hdrerror;
|
|---|
| 157 | }
|
|---|
| 158 | modulename[modsize] = 0;
|
|---|
| 159 | windlldep = Win32DllBase::findModule(modulename, TRUE);
|
|---|
| 160 | if(windlldep && strcmp(windlldep->getModuleName(), windll->getModuleName())) {
|
|---|
| 161 | dprintf(("RegisterLxDll: Add dependency %s -> %s", windll->getModuleName(), modulename));
|
|---|
| 162 | windll->addDependency(windlldep);
|
|---|
| 163 | }
|
|---|
| 164 | else dprintf(("HARMLESS WARNING: Can't find dll %s referenced by %s", modulename, windll->getModuleName()));
|
|---|
| 165 |
|
|---|
| 166 | offset += modsize + 1;
|
|---|
| 167 | }
|
|---|
| 168 | #ifdef HACK_NEVER_UNLOAD_LX_DLLS
|
|---|
| 169 | //HACK ALERT!!
|
|---|
| 170 | //This makes sure the LX dll never gets unloaded.
|
|---|
| 171 | //Necessary since unloading doesn't work due to dependencies on dlls
|
|---|
| 172 | //with exitlist handlers.
|
|---|
| 173 | windll->AddRef();
|
|---|
| 174 | #endif
|
|---|
| 175 | return windll->getInstanceHandle();
|
|---|
| 176 |
|
|---|
| 177 | hdrerror:
|
|---|
| 178 | dprintf(("DosQueryHeaderInfo returned %d", rc));
|
|---|
| 179 | return windll->getInstanceHandle();
|
|---|
| 180 | }
|
|---|
| 181 | //******************************************************************************
|
|---|
| 182 | //Destroy LX Dll object
|
|---|
| 183 | //******************************************************************************
|
|---|
| 184 | BOOL WIN32API UnregisterLxDll(HINSTANCE hInstance)
|
|---|
| 185 | {
|
|---|
| 186 | Win32DllBase *windll;
|
|---|
| 187 |
|
|---|
| 188 | //Don't proceed for pe2lx/win32k (os/2 dll unload dependency bug)
|
|---|
| 189 | //Don't do it either after ExitProcess has been called
|
|---|
| 190 | if(!fPeLoader || WinExe == NULL)
|
|---|
| 191 | return TRUE;
|
|---|
| 192 |
|
|---|
| 193 | windll = Win32DllBase::findModule(hInstance);
|
|---|
| 194 | if(!windll) {
|
|---|
| 195 | dprintf(("UnregisterLxDll: Can't find dll with handle %x (already deleted)", hInstance));
|
|---|
| 196 | return TRUE; //already deleted by Win32LxDll::Release
|
|---|
| 197 | }
|
|---|
| 198 | dprintf(("UnregisterLxDll %s", windll->getModuleName()));
|
|---|
| 199 | //This can only happen for LX dependencies (i.e. wininet loads wsock32)
|
|---|
| 200 | delete windll;
|
|---|
| 201 | return TRUE;
|
|---|
| 202 | }
|
|---|
| 203 | //******************************************************************************
|
|---|
| 204 | //******************************************************************************
|
|---|
| 205 | Win32LxDll::Win32LxDll(HINSTANCE hInstance, WIN32DLLENTRY EntryPoint, PVOID pResData,
|
|---|
| 206 | DWORD MajorImageVersion, DWORD MinorImageVersion,
|
|---|
| 207 | DWORD Subsystem)
|
|---|
| 208 | : Win32ImageBase(hInstance),
|
|---|
| 209 | Win32LxImage(hInstance, pResData),
|
|---|
| 210 | Win32DllBase(hInstance, EntryPoint)
|
|---|
| 211 | {
|
|---|
| 212 | this->MajorImageVersion = MajorImageVersion;
|
|---|
| 213 | this->MinorImageVersion = MinorImageVersion;
|
|---|
| 214 | this->Subsystem = Subsystem;
|
|---|
| 215 |
|
|---|
| 216 | if(EntryPoint == NULL) {
|
|---|
| 217 | fSkipThreadEntryCalls = TRUE;
|
|---|
| 218 | fAttachedToProcess = TRUE;
|
|---|
| 219 | }
|
|---|
| 220 | hinstanceOS2 = hInstance;
|
|---|
| 221 | //new win32 instance handle must be pointer to PE header
|
|---|
| 222 | hinstance = (HINSTANCE)buildHeader(MajorImageVersion, MinorImageVersion,
|
|---|
| 223 | Subsystem);
|
|---|
| 224 | dprintf(("Fake PE header %x for dll %s", hinstance, getModuleName()));
|
|---|
| 225 | }
|
|---|
| 226 | //******************************************************************************
|
|---|
| 227 | //******************************************************************************
|
|---|
| 228 | Win32LxDll::~Win32LxDll()
|
|---|
| 229 | {
|
|---|
| 230 | }
|
|---|
| 231 | //******************************************************************************
|
|---|
| 232 | //Returns reference count or -1 if load failed (PE loader only!)
|
|---|
| 233 | //******************************************************************************
|
|---|
| 234 | #ifdef DEBUG
|
|---|
| 235 | ULONG Win32LxDll::AddRef(char *parentname)
|
|---|
| 236 | #else
|
|---|
| 237 | ULONG Win32LxDll::AddRef()
|
|---|
| 238 | #endif
|
|---|
| 239 | {
|
|---|
| 240 | Win32DllBase *dll;
|
|---|
| 241 | QueueItem *item;
|
|---|
| 242 | ULONG ret;
|
|---|
| 243 |
|
|---|
| 244 | #ifdef DEBUG
|
|---|
| 245 | ret = Win32DllBase::AddRef(parentname);
|
|---|
| 246 | #else
|
|---|
| 247 | ret = Win32DllBase::AddRef();
|
|---|
| 248 | #endif
|
|---|
| 249 |
|
|---|
| 250 | if(!fPeLoader)
|
|---|
| 251 | return ret;
|
|---|
| 252 |
|
|---|
| 253 | if(referenced == 1)
|
|---|
| 254 | {
|
|---|
| 255 | item = loadedDlls.Head();
|
|---|
| 256 | while(item) {
|
|---|
| 257 | dll = (Win32DllBase *)loadedDlls.getItem(item);
|
|---|
| 258 | if(dll == NULL) {
|
|---|
| 259 | dprintf(("ERROR: Win32DllBase::AddRef: dll item == NULL!!"));
|
|---|
| 260 | DebugInt3();
|
|---|
| 261 | return -1;
|
|---|
| 262 | }
|
|---|
| 263 | #ifdef DEBUG
|
|---|
| 264 | dll->AddRef(getModuleName());
|
|---|
| 265 | #else
|
|---|
| 266 | dll->AddRef();
|
|---|
| 267 | #endif
|
|---|
| 268 | item = loadedDlls.getNext(item);
|
|---|
| 269 | }
|
|---|
| 270 | if(attachProcess() == 0) {
|
|---|
| 271 | dprintf(("WARNING: Dll %s refused to be loaded; aborting", getName()));
|
|---|
| 272 | return -1;
|
|---|
| 273 | }
|
|---|
| 274 | }
|
|---|
| 275 | return ret;
|
|---|
| 276 | }
|
|---|
| 277 | //******************************************************************************
|
|---|
| 278 | //******************************************************************************
|
|---|
| 279 | ULONG Win32LxDll::Release()
|
|---|
| 280 | {
|
|---|
| 281 | HINSTANCE hinst = hinstanceOS2;
|
|---|
| 282 | HINSTANCE hinstOdin = hinstance;
|
|---|
| 283 | WIN32DLLENTRY EntryPointTmp = dllEntryPoint;
|
|---|
| 284 | PVOID pResDataTmp = (PVOID)pResRootDir;
|
|---|
| 285 | DWORD MajorImageVersionTmp = MajorImageVersion;
|
|---|
| 286 | DWORD MinorImageVersionTmp = MinorImageVersion;
|
|---|
| 287 | DWORD SubsystemTmp = Subsystem;
|
|---|
| 288 | BOOL fNoUnload = fDisableUnload; //only set for kernel32.dll
|
|---|
| 289 | BOOL fDynLoaded = isDynamicLib();
|
|---|
| 290 | ULONG ret;
|
|---|
| 291 | APIRET rc;
|
|---|
| 292 |
|
|---|
| 293 | ret = Win32DllBase::Release();
|
|---|
| 294 |
|
|---|
| 295 | /** @sketch
|
|---|
| 296 | * If this module is not unreference and it was loaded using LoadLibrary() then
|
|---|
| 297 | * call the custombuild callback if present.
|
|---|
| 298 | * The callback should call UnRegisterLxDll().
|
|---|
| 299 | * Endif
|
|---|
| 300 | *
|
|---|
| 301 | * @remark
|
|---|
| 302 | * #ifdef HACK_NEVER_UNLOAD_LX_DLLS
|
|---|
| 303 | * This will never be called!
|
|---|
| 304 | * #endif
|
|---|
| 305 | *
|
|---|
| 306 | * @todo: call pfnLxDllLoadCallback if DosFreeModule failes.
|
|---|
| 307 | * It's not implemented because it's complex and at the moment we will
|
|---|
| 308 | * never actually get here.
|
|---|
| 309 | * So, this callback is here just as a reminder eventhough it's working..
|
|---|
| 310 | */
|
|---|
| 311 | if (!ret && fDynLoaded && pfnLxDllUnLoadCallback)
|
|---|
| 312 | {
|
|---|
| 313 | pfnLxDllUnLoadCallback(hinstanceOS2, hinstance);
|
|---|
| 314 | }
|
|---|
| 315 |
|
|---|
| 316 | if (ret == 0 && !fNoUnload) {//only set for kernel32.dll (fDisableUnload)
|
|---|
| 317 | //DosFreeModule sends a termination message to the dll.
|
|---|
| 318 | //The LX dll informs us when it's removed (UnregisterDll call)
|
|---|
| 319 | rc = DosFreeModule(hinst);
|
|---|
| 320 | if(rc) {
|
|---|
| 321 | dprintf(("Win32LxDll::Release: DosFreeModule %x returned %d", hinst, rc));
|
|---|
| 322 | if(rc == ERROR_INVALID_ACCESS && !fExitProcess)
|
|---|
| 323 | {
|
|---|
| 324 | #ifndef HACK_NEVER_UNLOAD_LX_DLLS
|
|---|
| 325 | #error @todo: call pfnLxDllLoadCallback and let it do the registering if it did that on the initial load..
|
|---|
| 326 | #endif
|
|---|
| 327 | //Dll refused to unload because it has an active exitlist handler
|
|---|
| 328 | //or depends on a dll that registered an exitlist handler
|
|---|
| 329 | //In this case the handle remains valid and the entrypoint of
|
|---|
| 330 | //the dll is NOT called for DLL_PROCESS_DETACH
|
|---|
| 331 | //WORKAROUND: Re-register the dll so future LoadLibrary calls
|
|---|
| 332 | // don't fail!
|
|---|
| 333 | dprintf(("WORKAROUND: Re-register the dll so future LoadLibrary calls don't fail!"));
|
|---|
| 334 | RegisterLxDll(hinst, EntryPointTmp, pResDataTmp,
|
|---|
| 335 | MajorImageVersionTmp,
|
|---|
| 336 | MinorImageVersionTmp,
|
|---|
| 337 | SubsystemTmp);
|
|---|
| 338 |
|
|---|
| 339 | /* OS/2 dll, system dll, converted dll or win32k took care of it. */
|
|---|
| 340 | Win32LxDll *pModule = Win32LxDll::findModuleByOS2Handle(hinst);
|
|---|
| 341 | if(pModule)
|
|---|
| 342 | {
|
|---|
| 343 | pModule->setDllHandleOS2(hinst);
|
|---|
| 344 | if(fPeLoader)
|
|---|
| 345 | {
|
|---|
| 346 | if(pModule->AddRef() == -1) {//-1 -> load failed (attachProcess)
|
|---|
| 347 | dprintf(("ERROR Dll refused to be loaded; aborting"));
|
|---|
| 348 | DebugInt3();
|
|---|
| 349 | delete pModule;
|
|---|
| 350 | return 0;
|
|---|
| 351 | }
|
|---|
| 352 | }
|
|---|
| 353 | pModule->incDynamicLib();
|
|---|
| 354 | }
|
|---|
| 355 | else DebugInt3();
|
|---|
| 356 | }
|
|---|
| 357 | }
|
|---|
| 358 | }
|
|---|
| 359 | return(ret);
|
|---|
| 360 | }
|
|---|
| 361 | //******************************************************************************
|
|---|
| 362 | //******************************************************************************
|
|---|
| 363 | BOOL Win32LxDll::isPe2LxDll() const
|
|---|
| 364 | {
|
|---|
| 365 | return FALSE;
|
|---|
| 366 | }
|
|---|
| 367 | //******************************************************************************
|
|---|
| 368 | //******************************************************************************
|
|---|
| 369 | BOOL Win32LxDll::isLxDll() const
|
|---|
| 370 | {
|
|---|
| 371 | return TRUE;
|
|---|
| 372 | }
|
|---|
| 373 | //******************************************************************************
|
|---|
| 374 | //******************************************************************************
|
|---|
| 375 | void Win32LxDll::setDllHandleOS2(HINSTANCE hInstanceOS2)
|
|---|
| 376 | {
|
|---|
| 377 | //Loaded with LoadLibrary(Ex); no need for a 2nd DosLoadModule
|
|---|
| 378 | //Dlls that are indirectly loaded (i.e. GDI32->KERNEL32 dependancy) need
|
|---|
| 379 | //this additional DosLoadModule (and setDllHandleOS2 isn't called for those)
|
|---|
| 380 | if(this->hinstanceOS2) {
|
|---|
| 381 | DosFreeModule(this->hinstanceOS2);
|
|---|
| 382 | }
|
|---|
| 383 | this->hinstanceOS2 = hInstanceOS2;
|
|---|
| 384 | }
|
|---|
| 385 | //******************************************************************************
|
|---|
| 386 | //******************************************************************************
|
|---|
| 387 | Win32LxDll *Win32LxDll::findModuleByOS2Handle(HINSTANCE hinstance)
|
|---|
| 388 | {
|
|---|
| 389 | dlllistmutex.enter();
|
|---|
| 390 |
|
|---|
| 391 | Win32DllBase *mod = Win32DllBase::getFirst();
|
|---|
| 392 | while (mod != NULL)
|
|---|
| 393 | {
|
|---|
| 394 | if (mod->isLxDll())
|
|---|
| 395 | {
|
|---|
| 396 | Win32LxDll *lxdll = (Win32LxDll *)mod;
|
|---|
| 397 | if (lxdll->hinstanceOS2 == hinstance)
|
|---|
| 398 | {
|
|---|
| 399 | dlllistmutex.leave();
|
|---|
| 400 | return(lxdll);
|
|---|
| 401 | }
|
|---|
| 402 | }
|
|---|
| 403 | mod = mod->getNext();
|
|---|
| 404 | }
|
|---|
| 405 | dlllistmutex.leave();
|
|---|
| 406 | return(NULL);
|
|---|
| 407 | }
|
|---|
| 408 | //******************************************************************************
|
|---|
| 409 | //******************************************************************************
|
|---|
| 410 |
|
|---|
| 411 | /**
|
|---|
| 412 | * Custombuild API for registering a callback for LX Dll loading thru LoadLibrary*().
|
|---|
| 413 | * @returns Success indicator.
|
|---|
| 414 | * @param pfn Pointer to callback.
|
|---|
| 415 | * NULL if callback is deregistered.
|
|---|
| 416 | */
|
|---|
| 417 | BOOL WIN32API ODIN_SetLxDllUnLoadCallback(PFNLXDLLUNLOAD pfn)
|
|---|
| 418 | {
|
|---|
| 419 | pfnLxDllUnLoadCallback = pfn;
|
|---|
| 420 | return TRUE;
|
|---|
| 421 | }
|
|---|
| 422 |
|
|---|