diff options
author | George Hazan <george.hazan@gmail.com> | 2013-08-30 18:38:04 +0000 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2013-08-30 18:38:04 +0000 |
commit | 2c73bb570b16ed862a3e28e263198273d4591074 (patch) | |
tree | 00fc4fb0d941aefebe955a25ae55fbca29325bd0 /plugins/ShellExt/src/shlcom.cpp | |
parent | a2d94febd339707a08bcfe02b995f26d71e8cf13 (diff) |
COM-related code rearranged
git-svn-id: http://svn.miranda-ng.org/main/trunk@5889 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/ShellExt/src/shlcom.cpp')
-rw-r--r-- | plugins/ShellExt/src/shlcom.cpp | 1060 |
1 files changed, 3 insertions, 1057 deletions
diff --git a/plugins/ShellExt/src/shlcom.cpp b/plugins/ShellExt/src/shlcom.cpp index 2b498f08e8..51d92832e0 100644 --- a/plugins/ShellExt/src/shlcom.cpp +++ b/plugins/ShellExt/src/shlcom.cpp @@ -4,22 +4,9 @@ #pragma comment(lib, "rpcrt4.lib")
-static bool VistaOrLater;
+bool VistaOrLater;
-struct SHLCOM
-{
- int FactoryCount, ObjectCount;
-
- SHLCOM() {
- FactoryCount = ObjectCount = 0;
- VistaOrLater = GetProcAddress( GetModuleHandleA("kernel32.dll"), "GetProductInfo") != NULL;
- }
-};
-
-static SHLCOM dllobject;
-
-#define IPC_PACKET_SIZE (0x1000 * 32)
-#define IPC_PACKET_NAME "m.mi.miranda.ipc.server"
+int DllFactoryCount, DllObjectCount;
struct TCMInvokeCommandInfo
{
@@ -59,1024 +46,14 @@ int IsCOMRegistered() /////////////////////////////////////////////////////////////////////////////////////////
-static char* CreateProcessUID(int pid, char *buf, size_t bufLen)
+char* CreateProcessUID(int pid, char *buf, size_t bufLen)
{
sprintf_s(buf, bufLen, "mim.shlext.%d$", pid);
return buf;
}
-static char* CreateUID(char *buf, size_t bufLen)
-{
- sprintf_s(buf, bufLen, "'mim.shlext.caller%d$%d", GetCurrentProcessId(), GetCurrentThreadId());
- return buf;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void FreeGroupTreeAndEmptyGroups(HMENU hParentMenu, TGroupNode *pp, TGroupNode *p)
-{
- while (p != NULL) {
- TGroupNode *q = p->Right;
- if (p->Left != NULL)
- FreeGroupTreeAndEmptyGroups(p->Left->hMenu, p, p->Left);
-
- if (p->dwItems == 0) {
- if (pp != NULL)
- DeleteMenu(pp->hMenu, p->hMenuGroupID, MF_BYCOMMAND);
- else
- DeleteMenu(hParentMenu, p->hMenuGroupID, MF_BYCOMMAND);
- }
- else
- // make sure this node's parent know's it exists
- if (pp != NULL)
- pp->dwItems++;
-
- free(p);
- p = q;
- }
-}
-
-void DecideMenuItemInfo(TSlotIPC *pct, TGroupNode *pg, MENUITEMINFOA &mii, TEnumData *lParam)
-{
- mii.wID = lParam->idCmdFirst;
- lParam->idCmdFirst++;
- // get the heap object
- HANDLE hDllHeap = lParam->Self->hDllHeap;
- TMenuDrawInfo *psd = (TMenuDrawInfo*)HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
- if (pct != NULL) {
- psd->cch = pct->cbStrSection - 1; // no null;
- psd->szText = (char*)HeapAlloc(hDllHeap, 0, pct->cbStrSection);
- lstrcpyA(psd->szText, (char*)pct + sizeof(TSlotIPC));
- psd->hContact = pct->hContact;
- psd->fTypes = dtContact;
- // find the protocol icon array to use && which status
- UINT c = lParam->Self->ProtoIconsCount;
- TSlotProtoIcons *pp = lParam->Self->ProtoIcons;
- psd->hStatusIcon = 0;
- while (c > 0) {
- c--;
- if (pp[c].hProto == pct->hProto && pp[c].pid == lParam->pid) {
- psd->hStatusIcon = pp[c].hIcons[pct->Status - ID_STATUS_OFFLINE];
- psd->hStatusBitmap = pp[c].hBitmaps[pct->Status - ID_STATUS_OFFLINE];
- break;
- }
- } // while
- psd->pid = lParam->pid;
- }
- else if (pg != NULL) {
- // store the given ID
- pg->hMenuGroupID = mii.wID;
- // steal the pointer from the group node it should be on the heap
- psd->cch = pg->cchGroup;
- psd->szText = pg->szGroup;
- psd->fTypes = dtGroup;
- } // if
- psd->wID = mii.wID;
- psd->szProfile = NULL;
- // store
- mii.dwItemData = UINT_PTR(psd);
-
- if (lParam->bOwnerDrawSupported && lParam->bShouldOwnerDraw) {
- mii.fType = MFT_OWNERDRAW;
- mii.dwTypeData = (LPSTR)psd;
- }
- else {
- // normal menu
- mii.fType = MFT_STRING;
- if (pct != NULL)
- mii.dwTypeData = LPSTR(pct) + sizeof(TSlotIPC);
- else
- mii.dwTypeData = pg->szGroup;
-
- // For Vista + let the system draw the theme && icons, pct = contact associated data
- if (VistaOrLater && pct != NULL && psd != NULL) {
- mii.fMask = MIIM_BITMAP | MIIM_FTYPE | MIIM_ID | MIIM_DATA | MIIM_STRING;
- // BuildSkinIcons() built an array of bitmaps which we can use here
- mii.hbmpItem = psd->hStatusBitmap;
- }
- } // if
-}
-
-// must be called after DecideMenuItemInfo()
-void BuildMRU(TSlotIPC *pct, MENUITEMINFOA &mii, TEnumData *lParam)
-{
- if (pct->MRU > 0) {
- lParam->Self->RecentCount++;
- // lParam->Self == pointer to object data
- InsertMenuItemA(lParam->Self->hRecentMenu, 0xFFFFFFFF, true, &mii);
- }
-}
-
-void BuildContactTree(TGroupNode *group, TEnumData *lParam)
-{
- // set up the menu item
- MENUITEMINFOA mii = { sizeof(mii) };
- mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;
-
- // go thru all the contacts
- TSlotIPC *pct = lParam->ipch->ContactsBegin;
- while (pct != NULL && pct->cbSize == sizeof(TSlotIPC) && pct->fType == REQUEST_CONTACTS) {
- if (pct->hGroup != 0) {
- // at the } of the slot header is the contact's display name
- // && after a double NULL char there is the group string, which has the full path of the group
- // this must be tokenised at '\' and we must walk the in memory group tree til we find our group
- // this is faster than the old version since we only ever walk one or at most two levels of the tree
- // per tokenised section, and it doesn't matter if two levels use the same group name (which is valid)
- // as the tokens processed is equatable to depth of the tree
-
- char *sz = strtok(LPSTR(UINT_PTR(pct) + sizeof(TSlotIPC) + UINT_PTR(pct->cbStrSection) + 1), "\\");
- // restore the root
- TGroupNode *pg = group;
- unsigned Depth = 0;
- while (sz != NULL) {
- UINT Hash = murmur_hash(sz);
- // find this node within
- while (pg != NULL) {
- // does this node have the right hash and the right depth?
- if (Hash == pg->Hash && Depth == pg->Depth)
- break;
- // each node may have a left pointer going to a sub tree
- // the path syntax doesn't know if a group is a group at the same level
- // or a nested one, which means the search node can be anywhere
- TGroupNode *px = pg->Left;
- if (px != NULL) {
- // keep searching this level
- while (px != NULL) {
- if (Hash == px->Hash && Depth == px->Depth) {
- // found the node we're looking for at the next level to pg, px is now pq for next time
- pg = px;
- goto grouploop;
- }
- px = px->Right;
- }
- }
- pg = pg->Right;
- }
-grouploop:
- Depth++;
- // process next token
- sz = strtok(NULL, "\\");
- }
- // tokenisation finished, if pg != NULL the group is found
- if (pg != NULL) {
- DecideMenuItemInfo(pct, NULL, mii, lParam);
- BuildMRU(pct, mii, lParam);
- InsertMenuItemA(pg->hMenu, 0xFFFFFFFF, true, &mii);
- pg->dwItems++;
- }
- }
- pct = pct->Next;
- }
-}
-
-void BuildMenuGroupTree(TGroupNode *p, TEnumData *lParam, HMENU hLastMenu)
-{
- MENUITEMINFOA mii;
- mii.cbSize = sizeof(MENUITEMINFO);
- mii.fMask = MIIM_ID | MIIM_DATA | MIIM_TYPE | MIIM_SUBMENU;
-
- // go thru each group and create a menu for it adding submenus too.
- while (p != NULL) {
- mii.hSubMenu = CreatePopupMenu();
- if (p->Left != NULL)
- BuildMenuGroupTree(p->Left, lParam, mii.hSubMenu);
- p->hMenu = mii.hSubMenu;
- DecideMenuItemInfo(NULL, p, mii, lParam);
- InsertMenuItemA(hLastMenu, 0xFFFFFFFF, true, &mii);
- p = p->Right;
- }
-}
-
-// this callback is triggered by the menu code and IPC is already taking place,
-// just the transfer type+data needs to be setup
-
-int __stdcall ClearMRUIPC(
- THeaderIPC *pipch, // IPC header info, already mapped
- HANDLE hWorkThreadEvent, // event object being waited on on miranda thread
- HANDLE hAckEvent, // ack event object that has been created
- TMenuDrawInfo *psd) // command/draw info
-{
- ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_CLEARMRU);
- ipcSendRequest(hWorkThreadEvent, hAckEvent, pipch, 100);
- return S_OK;
-}
-
-void RemoveCheckmarkSpace(HMENU HMENU)
-{
- if (!VistaOrLater)
- return;
-
- MENUINFO mi;
- mi.cbSize = sizeof(mi);
- mi.fMask = MIM_STYLE;
- mi.dwStyle = MNS_CHECKORBMP;
- SetMenuInfo(HMENU, &mi);
-}
-
-void BuildMenus(TEnumData *lParam)
-{
- HMENU hGroupMenu;
- LPSTR Token;
- TMenuDrawInfo *psd;
- UINT c;
- TSlotProtoIcons *pp;
-
- MENUITEMINFOA mii = { 0 };
- HANDLE hDllHeap = lParam->Self->hDllHeap;
- HMENU hBaseMenu = lParam->Self->hRootMenu;
-
- // build an in memory tree of the groups
- TGroupNodeList j = { 0, 0 };
- TSlotIPC *pg = lParam->ipch->GroupsBegin;
- while (pg != NULL) {
- if (pg->cbSize != sizeof(TSlotIPC) || pg->fType != REQUEST_GROUPS)
- break;
-
- UINT Depth = 0;
- TGroupNode *p = j.First; // start at root again
- // get the group
- Token = strtok(LPSTR(pg) + sizeof(TSlotIPC), "\\");
- while (Token != NULL) {
- UINT Hash = murmur_hash(Token);
- // if the (sub)group doesn't exist, create it.
- TGroupNode *q = FindGroupNode(p, Hash, Depth);
- if (q == NULL) {
- q = AllocGroupNode(&j, p, Depth);
- q->Depth = Depth;
- // this is the hash of this group node, but it can be anywhere
- // i.e. Foo\Foo this is because each node has a different depth
- // trouble is contacts don't come with depths!
- q->Hash = Hash;
- // don't assume that pg->hGroup's hash is valid for this token
- // since it maybe Miranda\Blah\Blah and we have created the first node
- // which maybe Miranda, thus giving the wrong hash
- // since "Miranda" can be a group of it's own and a full path
- q->cchGroup = lstrlenA(Token);
- q->szGroup = (LPSTR)HeapAlloc(hDllHeap, 0, q->cchGroup + 1);
- lstrcpyA(q->szGroup, Token);
- q->dwItems = 0;
- }
- p = q;
- Depth++;
- Token = strtok(NULL, "\\");
- }
- pg = pg->Next;
- }
-
- // build the menus inserting into hGroupMenu which will be a submenu of
- // the instance menu item. e.g. Miranda -> [Groups ->] contacts
- hGroupMenu = CreatePopupMenu();
-
- // allocate MRU menu, this will be associated with the higher up menu
- // so doesn't need to be freed (unless theres no MRUs items attached)
- // This menu is per process but the handle is stored globally (like a stack)
- lParam->Self->hRecentMenu = CreatePopupMenu();
- lParam->Self->RecentCount = 0;
- // create group menus only if they exist!
- if (lParam->ipch->GroupsBegin != NULL) {
- BuildMenuGroupTree(j.First, lParam, hGroupMenu);
- // add contacts that have a group somewhere
- BuildContactTree(j.First, lParam);
- }
-
- mii.cbSize = sizeof(MENUITEMINFO);
- mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;
- // add all the contacts that have no group (which maybe all of them)
- pg = lParam->ipch->ContactsBegin;
- while (pg != NULL) {
- if (pg->cbSize != sizeof(TSlotIPC) || pg->fType != REQUEST_CONTACTS)
- break;
- if (pg->hGroup == 0) {
- DecideMenuItemInfo(pg, NULL, mii, lParam);
- BuildMRU(pg, mii, lParam);
- InsertMenuItemA(hGroupMenu, 0xFFFFFFFF, true, &mii);
- }
- pg = pg->Next;
- }
-
- // insert MRU menu as a submenu of the contact menu only if
- // the MRU list has been created, the menu popup will be deleted by itself
- if (lParam->Self->RecentCount > 0) {
- // insert seperator and 'clear list' menu
- mii.fType = MFT_SEPARATOR;
- mii.fMask = MIIM_TYPE;
- InsertMenuItemA(lParam->Self->hRecentMenu, 0xFFFFFFFF, true, &mii);
-
- // insert 'clear MRU' item and setup callback
- mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA;
- mii.wID = lParam->idCmdFirst;
- lParam->idCmdFirst++;
- mii.fType = MFT_STRING;
- mii.dwTypeData = lParam->ipch->ClearEntries; // "Clear entries"
- // allocate menu substructure
- psd = (TMenuDrawInfo*)HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
- psd->fTypes = dtCommand;
- psd->MenuCommandCallback = &ClearMRUIPC;
- psd->wID = mii.wID;
- // this is needed because there is a clear list command per each process.
- psd->pid = lParam->pid;
- mii.dwItemData = (LPARAM)psd;
- InsertMenuItemA(lParam->Self->hRecentMenu, 0xFFFFFFFF, true, &mii);
-
- // insert MRU submenu into group menu (with) ownerdraw support as needed
- psd = (TMenuDrawInfo*)HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
- psd->szProfile = "MRU";
- psd->fTypes = dtGroup;
- // the IPC string pointer wont be around forever, must make a copy
- psd->cch = (int)strlen(lParam->ipch->MRUMenuName);
- psd->szText = (LPSTR)HeapAlloc(hDllHeap, 0, psd->cch + 1);
- lstrcpynA(psd->szText, lParam->ipch->MRUMenuName, sizeof(lParam->ipch->MRUMenuName) - 1);
-
- mii.dwItemData = (LPARAM)psd;
- if (lParam->bOwnerDrawSupported && lParam->bShouldOwnerDraw) {
- mii.fType = MFT_OWNERDRAW;
- mii.dwTypeData = (LPSTR)psd;
- }
- else mii.dwTypeData = lParam->ipch->MRUMenuName; // 'Recent';
-
- mii.wID = lParam->idCmdFirst;
- lParam->idCmdFirst++;
- mii.fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_DATA | MIIM_ID;
- mii.hSubMenu = lParam->Self->hRecentMenu;
- InsertMenuItemA(hGroupMenu, 0, true, &mii);
- }
- else {
- // no items were attached to the MRU, delete the MRU menu
- DestroyMenu(lParam->Self->hRecentMenu);
- lParam->Self->hRecentMenu = 0;
- }
-
- // allocate display info/memory for "Miranda" string
-
- mii.cbSize = sizeof(MENUITEMINFO);
- if (VistaOrLater)
- mii.fMask = MIIM_ID | MIIM_DATA | MIIM_FTYPE | MIIM_SUBMENU | MIIM_STRING | MIIM_BITMAP;
- else
- mii.fMask = MIIM_ID | MIIM_DATA | MIIM_TYPE | MIIM_SUBMENU;
-
- mii.hSubMenu = hGroupMenu;
-
- // by default, the menu will have space for icons and checkmarks (on Vista+) && we don't need this
- RemoveCheckmarkSpace(hGroupMenu);
-
- psd = (TMenuDrawInfo*)HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
- psd->cch = (int)strlen(lParam->ipch->MirandaName);
- psd->szText = (LPSTR)HeapAlloc(hDllHeap, 0, psd->cch + 1);
- lstrcpynA(psd->szText, lParam->ipch->MirandaName, sizeof(lParam->ipch->MirandaName) - 1);
- // there may not be a profile name
- pg = lParam->ipch->DataPtr;
- psd->szProfile = NULL;
- if (pg != NULL && pg->Status == STATUS_PROFILENAME) {
- psd->szProfile = (LPSTR)HeapAlloc(hDllHeap, 0, pg->cbStrSection);
- lstrcpyA(psd->szProfile, LPSTR(UINT_PTR(pg) + sizeof(TSlotIPC)));
- }
-
- // owner draw menus need ID's
- mii.wID = lParam->idCmdFirst;
- lParam->idCmdFirst++;
- psd->fTypes = dtEntry;
- psd->wID = mii.wID;
- psd->hContact = 0;
-
- // get Miranda's icon or bitmap
- c = lParam->Self->ProtoIconsCount;
- pp = lParam->Self->ProtoIcons;
- while (c > 0) {
- c--;
- if (pp[c].pid == lParam->pid && pp[c].hProto == 0) {
- // either of these can be 0
- psd->hStatusIcon = pp[c].hIcons[0];
- mii.hbmpItem = pp[c].hBitmaps[0];
- break;
- }
- }
- mii.dwItemData = (UINT_PTR)psd;
- if (lParam->bOwnerDrawSupported && lParam->bShouldOwnerDraw) {
- mii.fType = MFT_OWNERDRAW;
- mii.dwTypeData = (LPSTR)psd;
- }
- else {
- mii.fType = MFT_STRING;
- mii.dwTypeData = lParam->ipch->MirandaName;
- mii.cch = sizeof(lParam->ipch->MirandaName) - 1;
- }
- // add it all
- InsertMenuItemA(hBaseMenu, 0, true, &mii);
- // free the group tree
- FreeGroupTreeAndEmptyGroups(hGroupMenu, NULL, j.First);
-}
-
-void BuildSkinIcons(TEnumData *lParam)
-{
- IWICImagingFactory *factory = (VistaOrLater) ? ARGB_GetWorker() : NULL;
-
- TSlotIPC *pct = lParam->ipch->NewIconsBegin;
- TShlComRec *Self = lParam->Self;
- while (pct != NULL) {
- if (pct->cbSize != sizeof(TSlotIPC) || pct->fType != REQUEST_NEWICONS)
- break;
-
- TSlotProtoIcons *p = (TSlotProtoIcons*)(PBYTE(pct) + sizeof(TSlotIPC));
- Self->ProtoIcons = (TSlotProtoIcons*)realloc(Self->ProtoIcons, (Self->ProtoIconsCount + 1) * sizeof(TSlotProtoIcons));
- TSlotProtoIcons *d = &Self->ProtoIcons[Self->ProtoIconsCount];
- memmove(d, p, sizeof(TSlotProtoIcons));
-
- // if using Vista (or later), clone all the icons into bitmaps and keep these around,
- // if using anything older, just use the default code, the bitmaps (and/or icons) will be freed
- // with the shell object.
-
- for (int j = 0; j < 10; j++) {
- if (VistaOrLater) {
- d->hBitmaps[j] = ARGB_BitmapFromIcon(factory, Self->hMemDC, p->hIcons[j]);
- d->hIcons[j] = NULL;
- }
- else {
- d->hBitmaps[j] = NULL;
- d->hIcons[j] = CopyIcon(p->hIcons[j]);
- }
- }
-
- Self->ProtoIconsCount++;
- pct = pct->Next;
- }
-
- if (factory)
- factory->Release();
-}
-
-BOOL __stdcall ProcessRequest(HWND hwnd, LPARAM param)
-{
- HANDLE hMirandaWorkEvent;
- int replyBits;
- 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.
- 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
- 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(false, 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;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-TShlComRec::TShlComRec()
-{
- RefCount = 1;
- hDllHeap = HeapCreate(0, 0, 0);
- hRootMenu = 0;
- hRecentMenu = 0;
- RecentCount = 0;
- idCmdFirst = 0;
- pDataObject = NULL;
- ProtoIcons = NULL;
- ProtoIconsCount = 0;
- // create an inmemory DC
- HDC DC = GetDC(0);
- hMemDC = CreateCompatibleDC(DC);
- ReleaseDC(0, DC);
- // keep count on the number of objects
- dllobject.ObjectCount++;
-}
-
-HRESULT TShlComRec::QueryInterface(REFIID riid, void **ppvObject)
-{
- // IShellExtInit is given when the TShlRec is created
- if (riid == IID_IContextMenu || riid == IID_IContextMenu2 || riid == IID_IContextMenu3) {
- *ppvObject = (IContextMenu3*)this;
- RefCount++;
- logA("TShlComRec[%p] retrieved as IContextMenu3: %d\n", this, RefCount);
- return S_OK;
- }
-
- // under XP, it may ask for IShellExtInit again, this fixes the -double- click to see menus issue
- // which was really just the object not being created
- if (riid == IID_IShellExtInit) {
- *ppvObject = (IShellExtInit*)this;
- RefCount++;
- logA("TShlComRec[%p] retrieved as IContextMenu3: %d\n", this, RefCount);
- return S_OK;
- }
-
- // and, finally, IUnknown
- if (riid == IID_IUnknown) {
- *ppvObject = this;
- RefCount++;
- logA("TShlComRec[%p] retrieved as IUnknown: %d\n", this, RefCount);
- return S_OK;
- }
-
- *ppvObject = NULL;
- #ifdef LOG_ENABLED
- RPC_CSTR szGuid;
- UuidToStringA(&riid, &szGuid);
- logA("TShlComRec[%p] failed as {%s}\n", this, szGuid);
- RpcStringFreeA(&szGuid);
- #endif
- return E_NOINTERFACE;
-}
-
-ULONG TShlComRec::AddRef()
-{
- RefCount++;
- logA("TShlComRec[%p] added ref: %d\n", this, RefCount);
- return RefCount;
-}
-
-ULONG TShlComRec::Release()
-{
- ULONG ret = --RefCount;
- if (RefCount == 0) {
- // time to go byebye.
- // Note MRU menu is associated with a window (indirectly) so windows will free it.
- // free icons!
- if (ProtoIcons != NULL) {
- ULONG c = ProtoIconsCount;
- while (c > 0) {
- c--;
- TSlotProtoIcons *p = &ProtoIcons[c];
- for (int j = 0; j < 10; j++) {
- if (p->hIcons[j] != 0)
- DestroyIcon(p->hIcons[j]);
- if (p->hBitmaps[j] != 0)
- DeleteObject(p->hBitmaps[j]);
- }
- }
- free(ProtoIcons);
- ProtoIcons = NULL;
- }
- // free IDataObject reference if pointer exists
- if (pDataObject != NULL) {
- pDataObject->Release();
- pDataObject = NULL;
- }
- // free the heap and any memory allocated on it
- HeapDestroy(hDllHeap);
- // destroy the DC
- if (hMemDC != 0)
- DeleteDC(hMemDC);
-
- // free the instance (class record) created
- logA("TShlComRec[%p] final release\n", this);
- delete this;
- dllobject.ObjectCount--;
- }
- else logA("TShlComRec[%p] release ref: %d\n", this, RefCount);
-
- return ret;
-}
-
-HRESULT TShlComRec::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
-{
- // DObj is a pointer to an instance of IDataObject which is a pointer itself
- // it contains a pointer to a function table containing the function pointer
- // address of GetData() - the instance data has to be passed explicitly since
- // all compiler magic has gone.
- if (pdtobj == NULL)
- return E_INVALIDARG;
-
- // if an instance already exists, free it.
- if (pDataObject != NULL)
- pDataObject->Release();
-
- // store the new one and AddRef() it
- pDataObject = pdtobj;
- pDataObject->AddRef();
- return S_OK;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-struct DllVersionInfo
-{
- DWORD cbSize;
- DWORD dwMajorVersion, dwMinorVersion, dwBuildNumber, dwPlatformID;
-};
-
-typedef HRESULT (__stdcall *pfnDllGetVersion)(DllVersionInfo*);
-
-HRESULT TShlComRec::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT _idCmdFirst, UINT _idCmdLast, UINT uFlags)
-{
- if (((LOWORD(uFlags) & CMF_VERBSONLY) != CMF_VERBSONLY) && ((LOWORD(uFlags) & CMF_DEFAULTONLY) != CMF_DEFAULTONLY)) {
- bool bMF_OWNERDRAW = false;
- // get the shell version
- pfnDllGetVersion DllGetVersionProc = (pfnDllGetVersion)GetProcAddress( GetModuleHandleA("shell32.dll"), "DllGetVersion");
- if (DllGetVersionProc != NULL) {
- DllVersionInfo dvi;
- dvi.cbSize = sizeof(dvi);
- if (DllGetVersionProc(&dvi) >= 0)
- // it's at least 4.00
- bMF_OWNERDRAW = (dvi.dwMajorVersion > 4) || (dvi.dwMinorVersion >= 71);
- }
-
- // if we're using Vista (or later), the ownerdraw code will be disabled, because the system draws the icons.
- if (VistaOrLater)
- bMF_OWNERDRAW = false;
-
- HANDLE hMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, IPC_PACKET_SIZE, IPC_PACKET_NAME);
- if (hMap != 0 && GetLastError() != ERROR_ALREADY_EXISTS) {
- TEnumData ed;
- // map the memory to this address space
- THeaderIPC *pipch = (THeaderIPC*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
- if (pipch != NULL) {
- // let the callback have instance vars
- ed.Self = this;
- // not used 'ere
- hRootMenu = hmenu;
- // store the first ID to offset with index for InvokeCommand()
- idCmdFirst = _idCmdFirst;
- // store the starting index to offset
- ed.bOwnerDrawSupported = bMF_OWNERDRAW;
- ed.bShouldOwnerDraw = true;
- ed.idCmdFirst = idCmdFirst;
- ed.ipch = pipch;
- // allocate a wait object so the ST can signal us, it can't be anon
- // since it has to used by OpenEvent()
- CreateUID(pipch->SignalEventName, sizeof(pipch->SignalEventName));
- // create the wait wait-for-wait object
- ed.hWaitFor = CreateEventA(NULL, false, false, pipch->SignalEventName);
- if (ed.hWaitFor != 0) {
- // enumerate all the top level windows to find all loaded MIRANDACLASS classes
- EnumWindows(&ProcessRequest, LPARAM(&ed));
- // close the wait-for-reply object
- CloseHandle(ed.hWaitFor);
- }
- // unmap the memory from this address space
- UnmapViewOfFile(pipch);
- }
- // close the mapping
- CloseHandle(hMap);
- // use the MSDN recommended way, thou there ain't much difference
- return MAKE_HRESULT(0, 0, (ed.idCmdFirst - _idCmdFirst) + 1);
- }
- }
-
- // same as giving a SEVERITY_SUCCESS, FACILITY_NULL, since that
- // just clears the higher bits, which is done anyway
- return MAKE_HRESULT(0, 0, 1);
-}
-
-HRESULT TShlComRec::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pReserved, LPSTR pszName, UINT cchMax)
-{
- return E_NOTIMPL;
-}
-
-HRESULT ipcGetFiles(THeaderIPC *pipch, IDataObject* pDataObject, HANDLE hContact)
-{
- FORMATETC fet;
- fet.cfFormat = CF_HDROP;
- fet.ptd = NULL;
- fet.dwAspect = DVASPECT_CONTENT;
- fet.lindex = -1;
- fet.tymed = TYMED_HGLOBAL;
-
- STGMEDIUM stgm;
- HRESULT hr = pDataObject->GetData(&fet, &stgm);
- if (hr == S_OK) {
- // FIX, actually lock the global object and get a pointer
- HANDLE hDrop = GlobalLock(stgm.hGlobal);
- if (hDrop != 0) {
- // get the maximum number of files
- UINT iFile, iFileMax = DragQueryFileA((HDROP)stgm.hGlobal, -1, NULL, 0);
- for (iFile = 0; iFile < iFileMax; iFile++) {
- // get the size of the file path
- int cbSize = DragQueryFileA((HDROP)stgm.hGlobal, iFile, NULL, 0);
- // get the buffer
- TSlotIPC *pct = ipcAlloc(pipch, cbSize + 1); // including null term
- // allocated?
- if (pct == NULL)
- break;
- // store the hContact
- pct->hContact = hContact;
- // copy it to the buffer
- DragQueryFileA((HDROP)stgm.hGlobal, iFile, LPSTR(pct) + sizeof(TSlotIPC), pct->cbStrSection);
- }
- // store the number of files
- pipch->Slots = iFile;
- GlobalUnlock(stgm.hGlobal);
- } // if hDrop check
- // release the mediumn the lock may of failed
- ReleaseStgMedium(&stgm);
- }
- return hr;
-}
-
-HRESULT RequestTransfer(TShlComRec *Self, int idxCmd)
-{
- // get the contact information
- MENUITEMINFOA mii;
- mii.cbSize = sizeof(MENUITEMINFO);
- mii.fMask = MIIM_ID | MIIM_DATA;
- if ( !GetMenuItemInfoA(Self->hRootMenu, Self->idCmdFirst + idxCmd, false, &mii))
- return E_INVALIDARG;
-
- // get the pointer
- TMenuDrawInfo *psd = (TMenuDrawInfo*)mii.dwItemData;
- // the ID stored in the item pointer and the ID for the menu must match
- if (psd == NULL || psd->wID != mii.wID)
- return E_INVALIDARG;
-
- // is there an IDataObject instance?
- HRESULT hr = E_INVALIDARG;
- if (Self->pDataObject != NULL) {
- // OpenEvent() the work object to see if the instance is still around
- char szBuf[100];
- HANDLE hTransfer = OpenEventA(EVENT_ALL_ACCESS, false, CreateProcessUID(psd->pid, szBuf, sizeof(szBuf)));
- if (hTransfer != 0) {
- // map the ipc file again
- HANDLE hMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, IPC_PACKET_SIZE, IPC_PACKET_NAME);
- if (hMap != 0 && GetLastError() != ERROR_ALREADY_EXISTS) {
- // map it to process
- THeaderIPC *pipch = (THeaderIPC*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
- if (pipch != NULL) {
- // create the name of the object to be signalled by the ST
- lstrcpyA(pipch->SignalEventName, CreateUID(szBuf, sizeof(szBuf)));
- // create it
- HANDLE hReply = CreateEventA(NULL, false, false, pipch->SignalEventName);
- if (hReply != 0) {
- if (psd->fTypes & dtCommand) {
- if (psd->MenuCommandCallback)
- hr = psd->MenuCommandCallback(pipch, hTransfer, hReply, psd);
- }
- else {
- // prepare the buffer
- ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_XFRFILES);
- // get all the files into the packet
- if (ipcGetFiles(pipch, Self->pDataObject, psd->hContact) == S_OK) {
- // need to wait for the ST to open the mapping object
- // since if we close it before it's opened it the data it
- // has will be undefined
- int replyBits = ipcSendRequest(hTransfer, hReply, pipch, 200);
- if (replyBits != REPLY_FAIL) // they got the files!
- hr = S_OK;
- }
- }
- // close the work object name
- CloseHandle(hReply);
- }
- // unmap it from this process
- UnmapViewOfFile(pipch);
- }
- // close the map
- CloseHandle(hMap);
- }
- // close the handle to the ST object name
- CloseHandle(hTransfer);
- }
- }
- return hr;
-}
-
-HRESULT TShlComRec::InvokeCommand(CMINVOKECOMMANDINFO *pici)
-{
- return RequestTransfer(this, LOWORD(UINT_PTR(pici->lpVerb)));
-}
-
-HRESULT TShlComRec::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
-{
- LRESULT Dummy;
- if (plResult == NULL)
- plResult = &Dummy;
-
- SIZE tS;
- HBRUSH hBr;
-
- *plResult = true;
- if (uMsg == WM_DRAWITEM && wParam == 0) {
- // either a main sub menu, a group menu or a contact
- DRAWITEMSTRUCT *dwi = (DRAWITEMSTRUCT*)lParam;
- TMenuDrawInfo *psd = (TMenuDrawInfo*)dwi->itemData;
- // don't fill
- SetBkMode(dwi->hDC, TRANSPARENT);
- // where to draw the icon?
- RECT icorc;
- icorc.left = 0;
- icorc.top = dwi->rcItem.top + ((dwi->rcItem.bottom - dwi->rcItem.top) / 2) - (16 / 2);
- icorc.right = icorc.left + 16;
- icorc.bottom = icorc.top + 16;
- // draw for groups
- if (psd->fTypes & (dtGroup | dtEntry)) {
- hBr = GetSysColorBrush(COLOR_MENU);
- FillRect(dwi->hDC, &dwi->rcItem, hBr);
- DeleteObject(hBr);
-
- if (dwi->itemState & ODS_SELECTED) {
- // only do this for entry menu types otherwise a black mask
- // is drawn under groups
- hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
- FillRect(dwi->hDC, &dwi->rcItem, hBr);
- DeleteObject(hBr);
- SetTextColor(dwi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
- }
- // draw icon
- if (dwi->itemState & ODS_SELECTED)
- hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
- else
- hBr = GetSysColorBrush(COLOR_MENU);
-
- DrawIconEx(dwi->hDC, icorc.left + 1, icorc.top, psd->hStatusIcon, 16, 16, 0, hBr, DI_NORMAL);
- DeleteObject(hBr);
-
- // draw the text
- dwi->rcItem.left += dwi->rcItem.bottom - dwi->rcItem.top - 2;
- DrawTextA(dwi->hDC, psd->szText, psd->cch, &dwi->rcItem, DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- // draw the name of the database text if it's there
- if (psd->szProfile != NULL) {
- GetTextExtentPoint32A(dwi->hDC, psd->szText, psd->cch, &tS);
- dwi->rcItem.left += tS.cx + 8;
- SetTextColor(dwi->hDC, GetSysColor(COLOR_GRAYTEXT));
- DrawTextA(dwi->hDC, psd->szProfile, lstrlenA(psd->szProfile), &dwi->rcItem, DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- }
- }
- else {
- // it's a contact!
- hBr = GetSysColorBrush(COLOR_MENU);
- FillRect(dwi->hDC, &dwi->rcItem, hBr);
- DeleteObject(hBr);
- if (dwi->itemState & ODS_SELECTED) {
- hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
- FillRect(dwi->hDC, &dwi->rcItem, hBr);
- DeleteObject(hBr);
- SetTextColor(dwi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
- }
- // draw icon
- if (dwi->itemState & ODS_SELECTED)
- hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
- else
- hBr = GetSysColorBrush(COLOR_MENU);
-
- DrawIconEx(dwi->hDC, icorc.left + 2, icorc.top, psd->hStatusIcon, 16, 16, 0, hBr, DI_NORMAL);
- DeleteObject(hBr);
-
- // draw the text
- dwi->rcItem.left += dwi->rcItem.bottom - dwi->rcItem.top + 1;
- DrawTextA(dwi->hDC, psd->szText, psd->cch, &dwi->rcItem, DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- }
- }
- else if (uMsg == WM_MEASUREITEM) {
- // don't check if it's really a menu
- MEASUREITEMSTRUCT *msi = (MEASUREITEMSTRUCT*)lParam;
- TMenuDrawInfo *psd = (TMenuDrawInfo*)msi->itemData;
- NONCLIENTMETRICS ncm;
- ncm.cbSize = (VistaOrLater) ? sizeof(ncm) : offsetof(NONCLIENTMETRICS, iPaddedBorderWidth);
- SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
- // create the font used in menus, this font should be cached somewhere really
- HFONT hFont = CreateFontIndirect(&ncm.lfMenuFont);
- // select in the font
- HFONT hOldFont = (HFONT)SelectObject(hMemDC, hFont);
- // default to an icon
- int dx = 16;
- // get the size 'n' account for the icon
- GetTextExtentPoint32A(hMemDC, psd->szText, psd->cch, &tS);
- dx += tS.cx;
- // main menu item?
- if (psd->szProfile != NULL) {
- GetTextExtentPoint32A(hMemDC, psd->szProfile, lstrlenA(psd->szProfile), &tS);
- dx += tS.cx;
- }
- // store it
- msi->itemWidth = dx + ncm.iMenuWidth;
- msi->itemHeight = ncm.iMenuHeight + 2;
- if (tS.cy > (int)msi->itemHeight)
- msi->itemHeight += tS.cy - msi->itemHeight;
- // clean up
- SelectObject(hMemDC, hOldFont);
- DeleteObject(hFont);
- }
- return S_OK;
-}
-
-HRESULT TShlComRec::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
-}
-
/////////////////////////////////////////////////////////////////////////////////////////
-struct TClassFactoryRec : public IClassFactory
-{
- TClassFactoryRec() :
- RefCount(1)
- {
- dllobject.FactoryCount++;
- }
-
- LONG RefCount;
-
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
- ULONG STDMETHODCALLTYPE AddRef(void);
- ULONG STDMETHODCALLTYPE Release(void);
-
- HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
- HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock);
-};
-
-HRESULT TClassFactoryRec::QueryInterface(REFIID riid, void **ppvObject)
-{
- #ifdef LOG_ENABLED
- RPC_CSTR szGuid;
- UuidToStringA(&riid, &szGuid);
- logA("TClassFactoryRec::QueryInterface {%08x-%04x-%04x-%08x%08x} failed\n", szGuid);
- RpcStringFreeA(&szGuid);
- #endif
- *ppvObject = NULL;
- return E_NOINTERFACE;
-}
-
-ULONG TClassFactoryRec::AddRef()
-{
- return ++RefCount;
-}
-
-ULONG TClassFactoryRec::Release()
-{
- ULONG result = --RefCount;
- if (result == 0) {
- logA("TClassFactoryRec released\n");
- delete this;
- dllobject.FactoryCount--;
- }
- return result;
-}
-
-HRESULT TClassFactoryRec::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
-{
- *ppvObject = NULL;
-
- if (pUnkOuter != NULL)
- return CLASS_E_NOAGGREGATION;
-
- // Before Vista, the system queried for a IShell interface queried for a context menu, Vista now
- // queries for a context menu (or a shell menu) QI()'s the other interface
- if (riid == IID_IContextMenu) {
- TShlComRec *p = new TShlComRec();
- *ppvObject = (IContextMenu3*)p;
- logA("TClassFactoryRec created as IContextMenu3: %p\n", p);
- return S_OK;
- }
- if (riid == IID_IShellExtInit) {
- TShlComRec *p = new TShlComRec();
- *ppvObject = (IShellExtInit*)p;
- logA("TClassFactoryRec created as IShellExtInit: %p\n", p);
- return S_OK;
- }
-
- return E_NOTIMPL;
-}
-
-HRESULT TClassFactoryRec::LockServer(BOOL)
-{
- return E_NOTIMPL;
-}
-
//
// IPC part
//
@@ -1541,37 +518,6 @@ void InvokeThreadServer() mir_forkthread(&ThreadServer, hMainThread);
}
-// exported functions
-
-const IID CLSID_ISHLCOM = { 0x72013A26, 0xA94C, 0x11d6, {0x85, 0x40, 0xA5, 0xE6, 0x29, 0x32, 0x71, 0x1D }};
-
-STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
-{
- if (rclsid == CLSID_ISHLCOM && riid == IID_IClassFactory && FindWindowA(MIRANDACLASS, NULL) != 0) {
- *ppv = new TClassFactoryRec();
- logA("DllGetClassObject succeeded\n");
- return S_OK;
- }
-
- #ifdef LOG_ENABLED
- RPC_CSTR szGuid;
- UuidToStringA(&riid, &szGuid);
- logA("DllGetClassObject {%08x-%04x-%04x-%08x%08x} failed\n", szGuid);
- RpcStringFreeA(&szGuid);
- #endif
-
- *ppv = NULL;
- return CLASS_E_CLASSNOTAVAILABLE;
-}
-
-STDAPI DllCanUnloadNow()
-{
- logA("DllCanUnloadNow: %d %d\n", dllobject.FactoryCount, dllobject.ObjectCount);
- if (dllobject.FactoryCount == 0 && dllobject.ObjectCount == 0)
- return S_OK;
- return S_FALSE;
-}
-
// helper functions
HRESULT RemoveCOMRegistryEntries()
|