From 939b54967dcda681318271d203eedd1dcf5ce934 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Wed, 27 Jun 2012 05:25:13 +0000 Subject: renamed as the rest git-svn-id: http://svn.miranda-ng.org/main/trunk@645 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Mir_core/modules.cpp | 644 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 644 insertions(+) create mode 100644 plugins/Mir_core/modules.cpp (limited to 'plugins/Mir_core/modules.cpp') diff --git a/plugins/Mir_core/modules.cpp b/plugins/Mir_core/modules.cpp new file mode 100644 index 0000000000..6a9aa54bcb --- /dev/null +++ b/plugins/Mir_core/modules.cpp @@ -0,0 +1,644 @@ +/* + +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; + +/////////////////////////////////////////////////////////////////////////////// +// HOOKS + +MIR_CORE_DLL(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; +} + +MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent) +{ + EnterCriticalSection(&csHooks); + if (pLastHook == (THook*)hEvent) + pLastHook = NULL; + + int idx; + if ((idx = hooks.getIndex((THook*)hEvent)) == -1) { + LeaveCriticalSection(&csHooks); + return 1; + } + + THook* 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; +} + +MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook) +{ + THook* p = (THook*)hEvent; + + EnterCriticalSection(&csHooks); + if (hooks.getIndex(p) != -1) + p->pfnHook = pfnHook; + LeaveCriticalSection(&csHooks); + return 0; +} + +MIR_CORE_DLL(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; +} + +MIR_CORE_DLL(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); +} + +MIR_CORE_DLL(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; +} + +MIR_CORE_DLL(HANDLE) HookEvent(const char* name, MIRANDAHOOK hookProc) +{ + return HookEventInt(1, name, hookProc, 0, 0); +} + +MIR_CORE_DLL(HANDLE) HookEventParam(const char* name, MIRANDAHOOKPARAM hookProc, LPARAM lParam) +{ + return HookEventInt(2, name, (MIRANDAHOOK)hookProc, 0, lParam); +} + +MIR_CORE_DLL(HANDLE) HookEventObj(const char* name, MIRANDAHOOKOBJ hookProc, void* object) +{ + return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0); +} + +MIR_CORE_DLL(HANDLE) HookEventObjParam(const char* name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam) +{ + return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam); +} + +MIR_CORE_DLL(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; +} + +MIR_CORE_DLL(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; +} + +MIR_CORE_DLL(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)); + UnhookEvent((HANDLE)((hooks[i]->id << 16) + j + 1)); + if (hooks[i]->subscriberCount == 0) + break; + } } } + + LeaveCriticalSection(&csHooks); +} + +MIR_CORE_DLL(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 = mir_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 = mir_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; +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc) +{ + return CreateServiceInt(0, name, serviceProc, 0, 0); +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam) +{ + return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, 0, lParam); +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object) +{ + return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0); +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam) +{ + return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam); +} + +MIR_CORE_DLL(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; +} + +MIR_CORE_DLL(int) ServiceExists(const char *name) +{ + if (name == NULL) + return FALSE; + + EnterCriticalSection(&csServices); + int ret = FindServiceByName(name) != NULL; + LeaveCriticalSection(&csServices); + return ret; +} + +MIR_CORE_DLL(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 + OutputDebugStringA("Missing service called: \t"); + OutputDebugStringA(name); + OutputDebugStringA("\n"); + #endif + + 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); +} + +MIR_CORE_DLL(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); +} + +MIR_CORE_DLL(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; +} + +MIR_CORE_DLL(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)); + DestroyServiceFunction((HANDLE)services[i]->nameHash); + } } + + LeaveCriticalSection(&csServices); +} + +MIR_CORE_DLL(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); +} + +/////////////////////////////////////////////////////////////////////////////// + +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) +{ + EnterCriticalSection(&csHooks); + for (int i=0; i < hooks.getCount(); i++) { + THook* 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 (int j=0; j < services.getCount(); j++) + mir_free(services[j]); + + services.destroy(); + LeaveCriticalSection(&csServices); + DeleteCriticalSection(&csServices); + CloseHandle(hMainThread); +} -- cgit v1.2.3