BOOL __stdcall ProcessRequest(HWND hwnd, LPARAM param) { char szBuf[MAX_PATH]; TEnumData *lParam = (TEnumData*)param; DWORD pid = 0; GetWindowThreadProcessId(hwnd, &pid); if (pid != 0) { // old system would get a window's pid and the module handle that created it // and try to OpenEvent() a event object name to it (prefixed with a string) // this was fine for most Oses (not the best way) but now actually compares // the class string (a bit slower) but should get rid of those bugs finally. HANDLE hMirandaWorkEvent = OpenEventA(EVENT_ALL_ACCESS, false, CreateProcessUID(pid, szBuf, sizeof(szBuf))); if (hMirandaWorkEvent != 0) { GetClassNameA(hwnd, szBuf, sizeof(szBuf)); if ( lstrcmpA(szBuf, MIRANDACLASS) != 0) { // opened but not valid. logA("ProcessRequest(%d, %p): class %s differs from %s\n", pid, hwnd, szBuf, MIRANDACLASS); CloseHandle(hMirandaWorkEvent); return true; } } // if the event object exists, a shlext.dll running in the instance must of created it. if (hMirandaWorkEvent != 0) { logA("ProcessRequest(%d, %p): window found\n", pid, hwnd); // prep the request ipcPrepareRequests(IPC_PACKET_SIZE, lParam->ipch, REQUEST_ICONS | REQUEST_GROUPS | REQUEST_CONTACTS | REQUEST_NEWICONS); // slots will be in the order of icon data, groups contacts, the first // slot will contain the profile name DWORD replyBits = ipcSendRequest(hMirandaWorkEvent, lParam->hWaitFor, lParam->ipch, 1000); // replyBits will be REPLY_FAIL if the wait timed out, or it'll be the request // bits as sent or a series of *_NOTIMPL bits where the request bit were, if there are no // contacts to speak of, don't bother showing this instance of Miranda } if (replyBits != REPLY_FAIL && lParam->ipch->ContactsBegin != NULL) { logA("ProcessRequest(%d, %p): IPC succeeded\n", pid, hwnd); // load the address again, the server side will always overwrite it lParam->ipch->pClientBaseAddress = lParam->ipch; // fixup all the pointers to be relative to the memory map // the base pointer of the client side version of the mapped file ipcFixupAddresses(lParam->ipch); // store the PID used to create the work event object // that got replied to -- this is needed since each contact // on the final menu maybe on a different instance and another OpenEvent() will be needed. lParam->pid = pid; // check out the user options from the server lParam->bShouldOwnerDraw = (lParam->ipch->dwFlags & HIPC_NOICONS) == 0; // process the icons BuildSkinIcons(lParam); // process other replies BuildMenus(lParam); } // close the work object CloseHandle(hMirandaWorkEvent); } } return true; }
// this function is called from an APC into the main thread void __stdcall ipcService(ULONG_PTR dwParam) { HANDLE hSignal; TSlotIPC *pct; LPSTR szBuf; char szGroupStr[32]; DBVARIANT dbv; LPSTR szMiranda; // try to open the file mapping object the caller must make sure no other // running instance is using this file HANDLE hMap = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, IPC_PACKET_NAME); if (hMap == 0) return; // map the file to this process THeaderIPC *pMMT = (THeaderIPC*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); // if it fails the caller should of had some timeout in wait if (pMMT != NULL && pMMT->cbSize == sizeof(THeaderIPC) && pMMT->dwVersion == PLUGIN_MAKE_VERSION(2, 0, 1, 2)) { // toggle the right bits int *bits = &pMMT->fRequests; // jump right to a worker thread for file processing? if (*bits & REQUEST_XFRFILES) { THeaderIPC *cloned = (THeaderIPC*)mir_alloc(IPC_PACKET_SIZE); // translate from client space to cloned heap memory pMMT->pServerBaseAddress = pMMT->pClientBaseAddress; pMMT->pClientBaseAddress = cloned; CopyMemory(cloned, pMMT, IPC_PACKET_SIZE); ipcFixupAddresses(true, cloned); DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &cloned->Param, THREAD_SET_CONTEXT, false, 0); mir_forkthread(&IssueTransferThread, cloned); goto Reply; } // the request was to clear the MRU entries, we have no return data if (*bits & REQUEST_CLEARMRU) { mir_forkthread(&ClearMRUThread, NULL); goto Reply; } // the IPC header may have pointers that need to be translated // in either case the supplied data area pointers has to be // translated to this address space. // the server base address is always removed to get an offset // to which the client base is added, this is what ipcFixupAddresses() does pMMT->pServerBaseAddress = pMMT->pClientBaseAddress; pMMT->pClientBaseAddress = pMMT; // translate to the server space map ipcFixupAddresses(true, pMMT); // store the address map offset so the caller can retranslate pMMT->pServerBaseAddress = pMMT; // return some options to the client if (db_get_b(0, SHLExt_Name, SHLExt_ShowNoIcons, 0) != 0) pMMT->dwFlags = HIPC_NOICONS; // see if we have a custom string for 'Miranda' szMiranda = "Miranda"; lstrcpynA(pMMT->MirandaName, szMiranda, sizeof(pMMT->MirandaName) - 1); // for the MRU menu szBuf = Translate("Recently"); lstrcpynA(pMMT->MRUMenuName, szBuf, sizeof(pMMT->MRUMenuName) - 1); // and a custom string for "clear entries" szBuf = Translate("Clear entries"); lstrcpynA(pMMT->ClearEntries, szBuf, sizeof(pMMT->ClearEntries) - 1); // if the group mode is on, check if they want the CList setting bool bGroupMode = (BST_CHECKED == db_get_b(0, SHLExt_Name, SHLExt_UseGroups, BST_UNCHECKED)); if (bGroupMode && BST_CHECKED == db_get_b(0, SHLExt_Name, SHLExt_UseCListSetting, BST_UNCHECKED)) bGroupMode = db_get_b(0, "CList", "UseGroups", true) != 0; int iSlot = 0; // return profile if set if (BST_UNCHECKED == db_get_b(0, SHLExt_Name, SHLExt_ShowNoProfile, BST_UNCHECKED)) { pct = ipcAlloc(pMMT, 50); if (pct != NULL) { // will actually return with .dat if there's space for it, not what the docs say pct->Status = STATUS_PROFILENAME; CallService(MS_DB_GETPROFILENAME, 49, UINT_PTR(pct) + sizeof(TSlotIPC)); } } if (*bits & REQUEST_NEWICONS) ipcGetSkinIcons(pMMT); if (*bits & REQUEST_GROUPS) { // return contact's grouping if it's present while (bGroupMode) { _itoa(iSlot, szGroupStr, 10); if ( db_get_s(0, "CListGroups", szGroupStr, &dbv) != 0) break; pct = ipcAlloc(pMMT, lstrlenA(dbv.pszVal + 1) + 1); // first byte has flags, need null term if (pct != NULL) { if (pMMT->GroupsBegin == NULL) pMMT->GroupsBegin = pct; pct->fType = REQUEST_GROUPS; pct->hContact = 0; szBuf = LPSTR(pct) + sizeof(TSlotIPC); // get the end of the slot lstrcpyA(szBuf, dbv.pszVal + 1); pct->hGroup = 0; db_free(&dbv); // free the string } else { // outta space db_free(&dbv); break; } iSlot++; } // if there was no space left, it'll } on null if (pct == NULL) *bits = (*bits | GROUPS_NOTIMPL) & ~REQUEST_GROUPS; } // SHOULD check slot space. if (*bits & REQUEST_CONTACTS) { if (!ipcGetSortedContacts(pMMT, &iSlot, bGroupMode)) // fail if there were no contacts AT ALL *bits = (*bits | CONTACTS_NOTIMPL) & ~REQUEST_CONTACTS; } // store the number of slots allocated pMMT->Slots = iSlot; Reply: // get the handle the caller wants to be signalled on hSignal = OpenEventA(EVENT_ALL_ACCESS, false, pMMT->SignalEventName); if (hSignal != 0) { SetEvent(hSignal); CloseHandle(hSignal); } UnmapViewOfFile(pMMT); } CloseHandle(hMap); }