/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2009 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "commonheaders.h" #include // list of hooks static int compareHooks(const THook* p1, const THook* p2) { return strcmp(p1->name, p2->name); } static LIST hooks(50, compareHooks); struct THookToMainThreadItem { THook* hook; HANDLE hDoneEvent; WPARAM wParam; LPARAM lParam; int result; }; // list of services struct TService { DWORD nameHash; HINSTANCE hOwner; union { MIRANDASERVICE pfnService; MIRANDASERVICEPARAM pfnServiceParam; MIRANDASERVICEOBJ pfnServiceObj; MIRANDASERVICEOBJPARAM pfnServiceObjParam; }; int flags; LPARAM lParam; void* object; char name[1]; }; LIST services(100, NumericKeySortT); typedef struct { HANDLE hDoneEvent; WPARAM wParam; LPARAM lParam; int result; const char *name; } TServiceToMainThreadItem; // other static variables static BOOL bServiceMode = FALSE; static CRITICAL_SECTION csHooks, csServices; static DWORD mainThreadId; static int hookId = 1; static HANDLE hMainThread; static HANDLE hMissingService; static THook *pLastHook = NULL; HINSTANCE GetInstByAddress(void* codePtr); void LangPackDropUnusedItems(void); void ParseCommandLine(); // core: IDD_WAITRESTART int LoadSystemModule(void); // core: m_system.h services int LoadNewPluginsModuleInfos(void); // core: preloading plugins int LoadNewPluginsModule(void); // core: N.O. plugins int LoadSslModule(void); int LoadNetlibModule(void); // core: network void NetlibInitSsl(void); int LoadLangPackModule(void); // core: translation int LoadProtocolsModule(void); // core: protocol manager int LoadAccountsModule(void); // core: account manager int LoadIgnoreModule(void); // protocol filter: ignore int LoadSendRecvUrlModule(void); //send/recv int LoadSendRecvEMailModule(void); //send/recv int LoadSendRecvAuthModule(void); //send/recv int LoadSendRecvFileModule(void); //send/recv int LoadContactListModule(void);// ui: clist int LoadOptionsModule(void); // ui: options dialog int LoadFindAddModule(void); // ui: search/add users int LoadSkinIcons(void); int LoadSkinSounds(void); int LoadSkinHotkeys(void); int LoadHelpModule(void); // ui: help stuff int LoadUserInfoModule(void); // ui: user info int LoadHistoryModule(void); // ui: history viewer int LoadAwayMsgModule(void); // ui: setting away messages int LoadVisibilityModule(void); // ui: visibility control int LoadCLUIModule(void); // ui: CList UI int LoadPluginOptionsModule(void); // ui: plugin viewer int LoadAddContactModule(void); // ui: authcontrol contacts int LoadIdleModule(void); // rnd: report idle information int LoadAutoAwayModule(void); // ui: away int LoadUserOnlineModule(void); // ui: online alert int LoadUtilsModule(void); // ui: utils (has a few window classes, like HyperLink) int LoadCLCModule(void); // window class: CLC control int LoadButtonModule(void); // window class: button class int LoadContactsModule(void); // random: contact int LoadFontserviceModule(void); // ui: font manager int LoadIcoLibModule(void); // ui: icons manager int LoadUpdateNotifyModule(void); // random: update notification int LoadServiceModePlugin(void); int LoadErrorsModule(); void UnloadUtilsModule(void); void UnloadButtonModule(void); void UnloadClcModule(void); void UnloadContactListModule(void); void UnloadEventsModule(void); void UnloadIdleModule(void); void UnloadLangPackModule(void); void UnloadSslModule(void); void UnloadNetlibModule(void); void UnloadNewPlugins(void); void UnloadUpdateNotifyModule(void); void UnloadIcoLibModule(void); void UnloadSkinSounds(void); void UnloadSkinHotkeys(void); void UnloadProtocolsModule(void); void UnloadAccountsModule(void); void UnloadErrorsModule(void); int LoadIcoTabsModule(); int LoadHeaderbarModule(); int LoadDescButtonModule(); int LoadDefaultModules(void) { //load order is very important for these if ( LoadSystemModule()) return 1; if ( LoadLangPackModule()) return 1; // langpack will be a system module in the new order so this is moved here ParseCommandLine(); // IDD_WAITRESTART need langpack so this is moved here if ( LoadUtilsModule()) return 1; //order not important for this, but no dependencies and no point in pluginising if ( LoadIcoTabsModule()) return 1; if ( LoadHeaderbarModule()) return 1; if ( LoadNewPluginsModuleInfos()) return 1; // database is available here if ( LoadButtonModule()) return 1; if ( LoadIcoLibModule()) return 1; if ( LoadSkinIcons()) return 1; // if ( LoadErrorsModule()) return 1; bServiceMode = LoadServiceModePlugin(); switch (bServiceMode) { case 1: return 0; // stop loading here case 0: break; default: return 1; } //this info will be available at LoadNewPluginsModule() INT_PTR *disableDefaultModule=(INT_PTR*)CallService(MS_PLUGINS_GETDISABLEDEFAULTARRAY, 0, 0); if ( LoadSkinSounds()) return 1; if ( LoadSkinHotkeys()) return 1; if ( LoadFontserviceModule()) return 1; if ( LoadDescButtonModule()) return 1; if ( LoadOptionsModule()) return 1; if ( LoadNetlibModule()) return 1; if ( LoadProtocolsModule()) return 1; LoadDbAccounts(); // retrieves the account array from a database if ( LoadContactsModule()) return 1; if ( LoadContactListModule()) return 1; if ( LoadAddContactModule()) return 1; if ( LoadNewPluginsModule()) return 1; // will call Load() on everything, clist will load first LangPackDropUnusedItems(); if ( !disableDefaultModule[DEFMOD_SSL]) if (LoadSslModule()) return 1; NetlibInitSsl(); if (LoadAccountsModule()) return 1; //order becomes less important below here if ( !disableDefaultModule[DEFMOD_UIFINDADD]) if (LoadFindAddModule()) return 1; if ( !disableDefaultModule[DEFMOD_UIUSERINFO]) if (LoadUserInfoModule()) return 1; if ( !disableDefaultModule[DEFMOD_SRURL]) if (LoadSendRecvUrlModule()) return 1; if ( !disableDefaultModule[DEFMOD_SREMAIL]) if (LoadSendRecvEMailModule()) return 1; if ( !disableDefaultModule[DEFMOD_SRAUTH]) if (LoadSendRecvAuthModule()) return 1; if ( !disableDefaultModule[DEFMOD_SRFILE]) if (LoadSendRecvFileModule()) return 1; if ( !disableDefaultModule[DEFMOD_UIHELP]) if (LoadHelpModule()) return 1; if ( !disableDefaultModule[DEFMOD_UIHISTORY]) if (LoadHistoryModule()) return 1; if ( !disableDefaultModule[DEFMOD_RNDIDLE]) if (LoadIdleModule()) return 1; if ( !disableDefaultModule[DEFMOD_RNDAUTOAWAY]) if (LoadAutoAwayModule()) return 1; if ( !disableDefaultModule[DEFMOD_RNDUSERONLINE]) if (LoadUserOnlineModule()) return 1; if ( !disableDefaultModule[DEFMOD_SRAWAY]) if (LoadAwayMsgModule()) return 1; if ( !disableDefaultModule[DEFMOD_RNDIGNORE]) if (LoadIgnoreModule()) return 1; if ( !disableDefaultModule[DEFMOD_UIVISIBILITY]) if (LoadVisibilityModule()) return 1; if ( !disableDefaultModule[DEFMOD_UPDATENOTIFY]) if (LoadUpdateNotifyModule()) return 1; return 0; } void UnloadDefaultModules(void) { UnloadAccountsModule(); UnloadNewPlugins(); UnloadProtocolsModule(); UnloadSkinSounds(); UnloadSkinHotkeys(); // UnloadErrorsModule(); UnloadIcoLibModule(); UnloadUtilsModule(); UnloadButtonModule(); UnloadClcModule(); UnloadContactListModule(); UnloadEventsModule(); UnloadIdleModule(); UnloadUpdateNotifyModule(); UnloadNetlibModule(); UnloadSslModule(); UnloadLangPackModule(); } int InitialiseModularEngine(void) { InitializeCriticalSection(&csHooks); InitializeCriticalSection(&csServices); mainThreadId=GetCurrentThreadId(); DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hMainThread, 0, FALSE, DUPLICATE_SAME_ACCESS); hMissingService = CreateHookableEvent(ME_SYSTEM_MISSINGSERVICE); return 0; } void DestroyModularEngine(void) { int i; THook* p; EnterCriticalSection(&csHooks); for (i=0; i < hooks.getCount(); i++) { p = hooks[i]; if (p->subscriberCount) mir_free(p->subscriber); DeleteCriticalSection(&p->csHook); mir_free(p); } hooks.destroy(); LeaveCriticalSection(&csHooks); DeleteCriticalSection(&csHooks); EnterCriticalSection(&csServices); for (i=0; i < services.getCount(); i++) mir_free(services[i]); services.destroy(); LeaveCriticalSection(&csServices); DeleteCriticalSection(&csServices); CloseHandle(hMainThread); } ///////////////////////////////HOOKS HANDLE CreateHookableEvent(const char *name) { THook* ret; int idx; if (name == NULL) return NULL; EnterCriticalSection(&csHooks); if ((idx = hooks.getIndex((THook*)name)) != -1) { LeaveCriticalSection(&csHooks); return NULL; } ret = (THook*)mir_alloc(sizeof(THook)); strncpy(ret->name, name, sizeof(ret->name)); ret->name[ MAXMODULELABELLENGTH-1 ] = 0; ret->id = hookId++; ret->subscriberCount = 0; ret->subscriber = NULL; ret->pfnHook = NULL; InitializeCriticalSection(&ret->csHook); hooks.insert(ret); LeaveCriticalSection(&csHooks); return (HANDLE)ret; } int DestroyHookableEvent(HANDLE hEvent) { int idx; THook* p; EnterCriticalSection(&csHooks); if (pLastHook == (THook*)hEvent) pLastHook = NULL; if ((idx = hooks.getIndex((THook*)hEvent)) == -1) { LeaveCriticalSection(&csHooks); return 1; } p = hooks[idx]; if (p->subscriberCount) { mir_free(p->subscriber); p->subscriber = NULL; p->subscriberCount = 0; } hooks.remove(idx); DeleteCriticalSection(&p->csHook); mir_free(p); LeaveCriticalSection(&csHooks); return 0; } int SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook) { THook* p = (THook*)hEvent; EnterCriticalSection(&csHooks); if (hooks.getIndex(p) != -1) p->pfnHook = pfnHook; LeaveCriticalSection(&csHooks); return 0; } int CallPluginEventHook(HINSTANCE hInst, HANDLE hEvent, WPARAM wParam, LPARAM lParam) { int returnVal = 0; THook* p = (THook*)hEvent; if (p == NULL) return -1; EnterCriticalSection(&p->csHook); for (int i = 0; i < p->subscriberCount; i++) { THookSubscriber* s = &p->subscriber[i]; if (s->hOwner != hInst) continue; switch (s->type) { case 1: returnVal = s->pfnHook(wParam, lParam); break; case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; default: continue; } if (returnVal) break; } if (p->subscriberCount == 0 && p->pfnHook != 0) returnVal = p->pfnHook(wParam, lParam); LeaveCriticalSection(&p->csHook); return returnVal; } int CallHookSubscribers(HANDLE hEvent, WPARAM wParam, LPARAM lParam) { int returnVal = 0; THook* p = (THook*)hEvent; if (p == NULL) return -1; EnterCriticalSection(&p->csHook); // NOTE: We've got the critical section while all this lot are called. That's mostly safe, though. for (int i = 0; i < p->subscriberCount; i++) { THookSubscriber* s = &p->subscriber[i]; switch (s->type) { case 1: returnVal = s->pfnHook(wParam, lParam); break; case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; default: continue; } if (returnVal) break; } // check for no hooks and call the default hook if any if (p->subscriberCount == 0 && p->pfnHook != 0) returnVal = p->pfnHook(wParam, lParam); LeaveCriticalSection(&p->csHook); return returnVal; } static int checkHook(HANDLE hHook) { if (hHook == NULL) return -1; EnterCriticalSection(&csHooks); if (pLastHook != hHook || !pLastHook) { if (hooks.getIndex((THook*)hHook) == -1) { LeaveCriticalSection(&csHooks); return -1; } pLastHook = (THook*)hHook; } LeaveCriticalSection(&csHooks); return 0; } static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam) { THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam; if (checkHook(item->hook) == -1) item->result = -1; else item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam); SetEvent(item->hDoneEvent); } int NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam) { extern HWND hAPCWindow; if ( GetCurrentThreadId() != mainThreadId) { THookToMainThreadItem item; item.hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); item.hook = (THook*)hEvent; item.wParam = wParam; item.lParam = lParam; QueueUserAPC(HookToMainAPCFunc, hMainThread, (ULONG_PTR)&item); PostMessage(hAPCWindow, WM_NULL, 0, 0); // let it process APC even if we're in a common dialog WaitForSingleObject(item.hDoneEvent, INFINITE); CloseHandle(item.hDoneEvent); return item.result; } return (checkHook(hEvent) == -1) ? -1 : CallHookSubscribers(hEvent, wParam, lParam); } static HANDLE HookEventInt(int type, const char* name, MIRANDAHOOK hookProc, void* object, LPARAM lParam) { int idx; THook* p; HANDLE ret; EnterCriticalSection(&csHooks); if ((idx = hooks.getIndex((THook*)name)) == -1) { #ifdef _DEBUG OutputDebugStringA("Attempt to hook: \t"); OutputDebugStringA(name); OutputDebugStringA("\n"); #endif LeaveCriticalSection(&csHooks); return NULL; } p = hooks[ idx ]; p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount+1)); p->subscriber[ p->subscriberCount ].type = type; p->subscriber[ p->subscriberCount ].pfnHook = hookProc; p->subscriber[ p->subscriberCount ].object = object; p->subscriber[ p->subscriberCount ].lParam = lParam; p->subscriber[ p->subscriberCount ].hOwner = GetInstByAddress(hookProc); p->subscriberCount++; ret = (HANDLE)((p->id << 16) | p->subscriberCount); LeaveCriticalSection(&csHooks); return ret; } HANDLE HookEvent(const char* name, MIRANDAHOOK hookProc) { return HookEventInt(1, name, hookProc, 0, 0); } HANDLE HookEventParam(const char* name, MIRANDAHOOKPARAM hookProc, LPARAM lParam) { return HookEventInt(2, name, (MIRANDAHOOK)hookProc, 0, lParam); } HANDLE HookEventObj(const char* name, MIRANDAHOOKOBJ hookProc, void* object) { return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0); } HANDLE HookEventObjParam(const char* name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam) { return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam); } HANDLE HookEventMessage(const char* name, HWND hwnd, UINT message) { int idx; THook* p; HANDLE ret; EnterCriticalSection(&csHooks); if ((idx = hooks.getIndex((THook*)name)) == -1) { #ifdef _DEBUG MessageBoxA(NULL, "Attempt to hook non-existant event", name, MB_OK); #endif LeaveCriticalSection(&csHooks); return NULL; } p = hooks[ idx ]; p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount+1)); p->subscriber[ p->subscriberCount ].type = 5; p->subscriber[ p->subscriberCount ].hwnd = hwnd; p->subscriber[ p->subscriberCount ].message = message; p->subscriberCount++; ret = (HANDLE)((p->id << 16) | p->subscriberCount); LeaveCriticalSection(&csHooks); return ret; } int UnhookEvent(HANDLE hHook) { int i; THook* p = NULL; int hookId = (int)hHook >> 16; int subscriberId = ((int)hHook & 0xFFFF) - 1; if (hHook == NULL) return 0; EnterCriticalSection(&csHooks); for (i = 0; i < hooks.getCount(); i++) { if (hooks[i]->id == hookId) { p = hooks[i]; break; } } if (p == NULL) { LeaveCriticalSection(&csHooks); return 1; } if (subscriberId >= p->subscriberCount || subscriberId < 0) { LeaveCriticalSection(&csHooks); return 1; } p->subscriber[subscriberId].type = 0; p->subscriber[subscriberId].pfnHook = NULL; p->subscriber[subscriberId].hOwner = NULL; while (p->subscriberCount && p->subscriber[p->subscriberCount-1].type == 0) p->subscriberCount--; if (p->subscriberCount == 0) { if (p->subscriber) mir_free(p->subscriber); p->subscriber = NULL; } LeaveCriticalSection(&csHooks); return 0; } void KillModuleEventHooks(HINSTANCE hInst) { int i, j; EnterCriticalSection(&csHooks); for (i = hooks.getCount()-1; i >= 0; i--) { if (hooks[i]->subscriberCount == 0) continue; for (j = hooks[i]->subscriberCount-1; j >= 0; j--) { if (hooks[i]->subscriber[j].hOwner == hInst) { char szModuleName[ MAX_PATH ]; GetModuleFileNameA(hooks[i]->subscriber[j].hOwner, szModuleName, sizeof(szModuleName)); Netlib_Logf(NULL, "A hook %08x for event '%s' was abnormally deleted because module '%s' didn't released it", hooks[i]->subscriber[j].pfnHook, hooks[i]->name, szModuleName); UnhookEvent((HANDLE)((hooks[i]->id << 16) + j + 1)); if (hooks[i]->subscriberCount == 0) break; } } } LeaveCriticalSection(&csHooks); } void KillObjectEventHooks(void* pObject) { int i, j; EnterCriticalSection(&csHooks); for (i = hooks.getCount()-1; i >= 0; i--) { if (hooks[i]->subscriberCount == 0) continue; for (j = hooks[i]->subscriberCount-1; j >= 0; j--) { if (hooks[i]->subscriber[j].object == pObject) { UnhookEvent((HANDLE)((hooks[i]->id << 16) + j + 1)); if (hooks[i]->subscriberCount == 0) break; } } } LeaveCriticalSection(&csHooks); } /////////////////////SERVICES static __inline TService* FindServiceByName(const char *name) { unsigned hash = hashstr(name); return services.find((TService*)&hash); } static HANDLE CreateServiceInt(int type, const char *name, MIRANDASERVICE serviceProc, void* object, LPARAM lParam) { if (name == NULL) return NULL; TService tmp; tmp.nameHash = hashstr(name); EnterCriticalSection(&csServices); if (services.getIndex(&tmp) != -1) { LeaveCriticalSection(&csServices); return NULL; } TService* p = (TService*)mir_alloc(sizeof(*p) + strlen(name)); strcpy(p->name, name); p->nameHash = tmp.nameHash; p->pfnService = serviceProc; p->hOwner = GetInstByAddress(serviceProc); p->flags = type; p->lParam = lParam; p->object = object; services.insert(p); LeaveCriticalSection(&csServices); return (HANDLE)tmp.nameHash; } HANDLE CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc) { return CreateServiceInt(0, name, serviceProc, 0, 0); } HANDLE CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam) { return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, 0, lParam); } HANDLE CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object) { return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0); } HANDLE CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam) { return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam); } int DestroyServiceFunction(HANDLE hService) { int idx; EnterCriticalSection(&csServices); if ((idx = services.getIndex((TService*)&hService)) != -1) { mir_free(services[idx]); services.remove(idx); } LeaveCriticalSection(&csServices); return 0; } int ServiceExists(const char *name) { if (name == NULL) return FALSE; EnterCriticalSection(&csServices); int ret = FindServiceByName(name) != NULL; LeaveCriticalSection(&csServices); return ret; } INT_PTR CallService(const char *name, WPARAM wParam, LPARAM lParam) { #ifdef _DEBUG if (name == NULL) { MessageBoxA(0, "Someone tried to CallService(NULL, ..) see stack trace for details", "", 0); DebugBreak(); return CALLSERVICE_NOTFOUND; } #else if (name == NULL) return CALLSERVICE_NOTFOUND; #endif EnterCriticalSection(&csServices); TService *pService = FindServiceByName(name); if (pService == NULL) { LeaveCriticalSection(&csServices); #ifdef _DEBUG //MessageBoxA(NULL, "Attempt to call non-existant service", name, MB_OK); OutputDebugStringA("Missing service called: \t"); OutputDebugStringA(name); OutputDebugStringA("\n"); #endif /* { MISSING_SERVICE_PARAMS params = { name, wParam, lParam }; int result = NotifyEventHooks(hMissingService, 0, (LPARAM)¶ms); if (result != 0) return params.lParam; } */ return CALLSERVICE_NOTFOUND; } MIRANDASERVICE pfnService = pService->pfnService; int flags = pService->flags; LPARAM fnParam = pService->lParam; void* object = pService->object; LeaveCriticalSection(&csServices); switch(flags) { case 1: return ((MIRANDASERVICEPARAM)pfnService)(wParam, lParam, fnParam); case 2: return ((MIRANDASERVICEOBJ)pfnService)(object, wParam, lParam); case 3: return ((MIRANDASERVICEOBJPARAM)pfnService)(object, wParam, lParam, fnParam); default: return pfnService(wParam, lParam); } } static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam) { TServiceToMainThreadItem *item = (TServiceToMainThreadItem*) dwParam; item->result = CallService(item->name, item->wParam, item->lParam); SetEvent(item->hDoneEvent); } INT_PTR CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam) { extern HWND hAPCWindow; if (name == NULL) return CALLSERVICE_NOTFOUND; // the service is looked up within the main thread, since the time it takes // for the APC queue to clear the service being called maybe removed. // even thou it may exists before the call, the critsec can't be locked between calls. if (GetCurrentThreadId() != mainThreadId) { TServiceToMainThreadItem item; item.wParam = wParam; item.lParam = lParam; item.name = name; item.hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); QueueUserAPC(CallServiceToMainAPCFunc, hMainThread, (ULONG_PTR) &item); PostMessage(hAPCWindow, WM_NULL, 0, 0); // let this get processed in its own time WaitForSingleObject(item.hDoneEvent, INFINITE); CloseHandle(item.hDoneEvent); return item.result; } return CallService(name, wParam, lParam); } int CallFunctionAsync(void (__stdcall *func)(void *), void *arg) { extern HWND hAPCWindow; int r = QueueUserAPC((void (__stdcall *)(ULONG_PTR))func, hMainThread, (ULONG_PTR)arg); PostMessage(hAPCWindow, WM_NULL, 0, 0); return r; } void KillModuleServices(HINSTANCE hInst) { int i; EnterCriticalSection(&csServices); for (i = services.getCount()-1; i >= 0; i--) { if (services[i]->hOwner == hInst) { char szModuleName[ MAX_PATH ]; GetModuleFileNameA(services[i]->hOwner, szModuleName, sizeof(szModuleName)); Netlib_Logf(NULL, "A service function '%s' was abnormally deleted because module '%s' didn't released it", services[i]->name, szModuleName); DestroyServiceFunction((HANDLE)services[i]->nameHash); } } LeaveCriticalSection(&csServices); } void KillObjectServices(void* pObject) { int i; EnterCriticalSection(&csServices); for (i = services.getCount()-1; i >= 0; i--) if (services[i]->object == pObject) DestroyServiceFunction((HANDLE)services[i]->nameHash); LeaveCriticalSection(&csServices); }