diff options
author | George Hazan <george.hazan@gmail.com> | 2013-08-20 21:43:39 +0000 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2013-08-20 21:43:39 +0000 |
commit | f7f6e4fed8860b0b66b2635000fe64de58a516c7 (patch) | |
tree | 6276817c7946ee5467774da2d4e14422584fb773 /plugins/ShellExt/src | |
parent | 0b551d05899081e6f235cddce0332fce9b6e30a5 (diff) |
ShellExt - first 1000 lines translated
git-svn-id: http://svn.miranda-ng.org/main/trunk@5766 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/ShellExt/src')
-rw-r--r-- | plugins/ShellExt/src/Version.h | 18 | ||||
-rw-r--r-- | plugins/ShellExt/src/main.cpp | 111 | ||||
-rw-r--r-- | plugins/ShellExt/src/options.cpp | 121 | ||||
-rw-r--r-- | plugins/ShellExt/src/resource.h | 19 | ||||
-rw-r--r-- | plugins/ShellExt/src/shlcom.cpp | 2122 | ||||
-rw-r--r-- | plugins/ShellExt/src/shlcom.h | 193 | ||||
-rw-r--r-- | plugins/ShellExt/src/stdafx.cpp | 18 | ||||
-rw-r--r-- | plugins/ShellExt/src/stdafx.h | 45 |
8 files changed, 2647 insertions, 0 deletions
diff --git a/plugins/ShellExt/src/Version.h b/plugins/ShellExt/src/Version.h new file mode 100644 index 0000000000..656c2324b5 --- /dev/null +++ b/plugins/ShellExt/src/Version.h @@ -0,0 +1,18 @@ +#define __MAJOR_VERSION 2
+#define __MINOR_VERSION 2
+#define __RELEASE_NUM 0
+#define __BUILD_NUM 1
+
+#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM
+#define __FILEVERSION_STRING_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM
+
+#define __STRINGIFY(x) #x
+#define __VERSION_STRING __STRINGIFY(__FILEVERSION_STRING_DOTS)
+
+#define __PLUGIN_NAME "ShellExt"
+#define __FILENAME "ShellExt.dll"
+#define __DESCRIPTION "Windows Explorer extension for Miranda NG."
+#define __AUTHOR "Sam Kothari, Miranda NG Team"
+#define __AUTHOREMAIL "egodust@users.sourceforge.net"
+#define __AUTHORWEB "http://miranda-ng.org/p/ShellExt/"
+#define __COPYRIGHT "© 2009 Sam Kothari (egoDust)"
diff --git a/plugins/ShellExt/src/main.cpp b/plugins/ShellExt/src/main.cpp new file mode 100644 index 0000000000..b2c3473172 --- /dev/null +++ b/plugins/ShellExt/src/main.cpp @@ -0,0 +1,111 @@ +#include "stdafx.h"
+
+HINSTANCE hInst;
+int hLangpack;
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {7993AB24-1FDA-428C-A89B-BE377A10BE3A}
+ {0x7993ab24, 0x1fda, 0x428c, {0xa8, 0x9b, 0xbe, 0x37, 0x7a, 0x10, 0xbe, 0x3a}}
+};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfoEx;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct HRegKey
+{
+ HRegKey(HKEY hRoot, const TCHAR *ptszKey) : m_key(NULL)
+ { RegCreateKeyEx(hRoot, ptszKey, 0, 0, 0, KEY_SET_VALUE | KEY_CREATE_SUB_KEY, 0, &m_key, 0);
+ }
+
+ ~HRegKey() { if (m_key) RegCloseKey(m_key); }
+
+ operator HKEY() const { return m_key; }
+
+private:
+ HKEY m_key;
+};
+
+char str1[] = "shlext " __VERSION_STRING " - shell context menu support for Miranda NG";
+char str2[] = "{72013A26-A94C-11d6-8540-A5E62932711D}";
+char str3[] = "miranda.shlext";
+char str4[] = "Apartment";
+
+TCHAR key1[] = _T("miranda.shlext\\{72013A26-A94C-11d6-8540-A5E62932711D}\\InprocServer32");
+
+HRESULT __stdcall DllRegisterServer()
+{
+ if ( RegSetValueA(HKEY_CLASSES_ROOT, "miranda.shlext", REG_SZ, str1, sizeof(str1)-1))
+ return E_FAIL;
+ if ( RegSetValueA(HKEY_CLASSES_ROOT, "miranda.shlext\\CLSID", REG_SZ, str2, sizeof(str2)-1))
+ return E_FAIL;
+ if ( RegSetValueA(HKEY_CLASSES_ROOT, "miranda.shlext\\{72013A26-A94C-11d6-8540-A5E62932711D}", REG_SZ, str3, sizeof(str3)-1))
+ return E_FAIL;
+ if ( RegSetValueA(HKEY_CLASSES_ROOT, "miranda.shlext\\{72013A26-A94C-11d6-8540-A5E62932711D}\\ProgID", REG_SZ, str3, sizeof(str3)-1))
+ return E_FAIL;
+
+ TCHAR tszFileName[MAX_PATH];
+ GetModuleFileName(hInst, tszFileName, SIZEOF(tszFileName));
+ if ( RegSetValue(HKEY_CLASSES_ROOT, key1, REG_SZ, tszFileName, lstrlen(tszFileName)))
+ return E_FAIL;
+
+ HRegKey k1(HKEY_CLASSES_ROOT, key1);
+ if (k1 == NULL)
+ return E_FAIL;
+ if ( RegSetValueA(k1, "ThreadingModel", REG_SZ, str4, sizeof(str4)))
+ return E_FAIL;
+
+ if ( RegSetValueA(HKEY_CLASSES_ROOT, "*\\shellex\\ContextMenuHandlers\\miranda.shlext", REG_SZ, str2, sizeof(str2)-1))
+ return E_FAIL;
+ if ( RegSetValueA(HKEY_CLASSES_ROOT, "Directory\\shellex\\ContextMenuHandlers\\miranda.shlext", REG_SZ, str2, sizeof(str2)-1))
+ return E_FAIL;
+
+ HRegKey k2(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"));
+ if (k2 == NULL)
+ return E_FAIL;
+ if ( RegSetValueA(k2, str2, REG_SZ, str1, sizeof(str1)-1))
+ return E_FAIL;
+
+ return S_OK;
+}
+
+HRESULT __stdcall DllUnregisterServer()
+{
+ return RemoveCOMRegistryEntries();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ mir_getLP(&pluginInfoEx);
+
+ InvokeThreadServer();
+ HookEvent(ME_OPT_INITIALISE, OnOptionsInit);
+ DllRegisterServer();
+ CheckRegisterServer();
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ return 0;
+}
diff --git a/plugins/ShellExt/src/options.cpp b/plugins/ShellExt/src/options.cpp new file mode 100644 index 0000000000..05f71ab039 --- /dev/null +++ b/plugins/ShellExt/src/options.cpp @@ -0,0 +1,121 @@ +#include "stdafx.h"
+#include "resource.h"
+
+static void AutoSize(HWND hwnd)
+{
+ HDC hDC = GetDC(hwnd);
+ HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ HFONT hOldFont = (HFONT)SelectObject(hDC, hFont);
+
+ TCHAR szBuf[MAX_PATH];
+ int i = GetWindowText(hwnd, szBuf, MAX_PATH);
+
+ SIZE tS;
+ GetTextExtentPoint32(hDC, szBuf, i, &tS);
+ SelectObject(hDC, hOldFont);
+ DeleteObject(hFont);
+ ReleaseDC(hwnd, hDC);
+ SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, tS.cx + 10, tS.cy, SWP_NOMOVE | SWP_FRAMECHANGED);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+static TCHAR* COM_OKSTR[2] = {
+ LPGENT("Problem, registration missing/deleted."),
+ LPGENT("Successfully created shell registration.") };
+static TCHAR* COM_APPROVEDSTR[2] = { LPGENT("Not Approved"), LPGENT("Approved") };
+
+static LRESULT CALLBACK OptDialogProc(HWND hwndDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
+{
+ int comReg, iCheck;
+ TCHAR szBuf[MAX_PATH];
+
+ switch(wMsg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ comReg = IsCOMRegistered();
+ mir_sntprintf(szBuf, SIZEOF(szBuf), _T("%s (%s)"),
+ COM_OKSTR[ (comReg & COMREG_OK) != 0 ],
+ COM_APPROVEDSTR[ (comReg & COMREG_APPROVED) != 0 ]);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_STATUS), szBuf);
+ // auto size the static windows to fit their text
+ // they're rendering in a font not selected into the DC.
+ AutoSize(GetDlgItem(hwndDlg, IDC_CAPMENUS));
+ AutoSize(GetDlgItem(hwndDlg, IDC_CAPSTATUS));
+ AutoSize(GetDlgItem(hwndDlg, IDC_CAPSHLSTATUS));
+ // show all the options
+ iCheck = db_get_b(0, MODULENAME, SHLExt_UseGroups, BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEGROUPS, iCheck);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CLISTGROUPS), iCheck = BST_CHECKED);
+ CheckDlgButton(hwndDlg, IDC_CLISTGROUPS,
+ db_get_b(0, MODULENAME, SHLExt_UseCListSetting, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_NOPROF,
+ db_get_b(0, MODULENAME, SHLExt_ShowNoProfile, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_SHOWFULL,
+ db_get_b(0, MODULENAME, SHLExt_UseHITContacts, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_SHOWINVISIBLES,
+ db_get_b(0, MODULENAME, SHLExt_UseHIT2Contacts, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_USEOWNERDRAW,
+ db_get_b(0, MODULENAME, SHLExt_ShowNoIcons, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_HIDEOFFLINE,
+ db_get_b(0, MODULENAME, SHLExt_ShowNoOffline, BST_UNCHECKED));
+ // give the Remove button a Vista icon
+ SendMessage(GetDlgItem(hwndDlg, IDC_REMOVE), BCM_SETSHIELD, 0, 1);
+ return TRUE;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY) {
+ db_set_b(0, MODULENAME, SHLExt_UseGroups, IsDlgButtonChecked(hwndDlg, IDC_USEGROUPS));
+ db_set_b(0, MODULENAME, SHLExt_UseCListSetting, IsDlgButtonChecked(hwndDlg, IDC_CLISTGROUPS));
+ db_set_b(0, MODULENAME, SHLExt_ShowNoProfile, IsDlgButtonChecked(hwndDlg, IDC_NOPROF));
+ db_set_b(0, MODULENAME, SHLExt_UseHITContacts, IsDlgButtonChecked(hwndDlg, IDC_SHOWFULL));
+ db_set_b(0, MODULENAME, SHLExt_UseHIT2Contacts, IsDlgButtonChecked(hwndDlg, IDC_SHOWINVISIBLES));
+ db_set_b(0, MODULENAME, SHLExt_ShowNoIcons, IsDlgButtonChecked(hwndDlg, IDC_USEOWNERDRAW));
+ db_set_b(0, MODULENAME, SHLExt_ShowNoOffline, IsDlgButtonChecked(hwndDlg, IDC_HIDEOFFLINE));
+ }
+ break;
+
+ case WM_COMMAND:
+ // don't send the changed message if remove is clicked
+ if ( LOWORD(wParam) != IDC_REMOVE)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+
+ switch( LOWORD(wParam)) {
+ case IDC_USEGROUPS:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CLISTGROUPS), BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_USEGROUPS));
+ break;
+ case IDC_REMOVE:
+ if (IDYES == MessageBox(0,
+ TranslateT("Are you sure? this will remove all the settings stored in your database && all registry entries created for shlext to work with Explorer"),
+ TranslateT("Disable/Remove shlext"), MB_YESNO | MB_ICONQUESTION)) {
+ db_unset(0, MODULENAME, SHLExt_UseGroups);
+ db_unset(0, MODULENAME, SHLExt_UseCListSetting);
+ db_unset(0, MODULENAME, SHLExt_UseHITContacts);
+ db_unset(0, MODULENAME, SHLExt_UseHIT2Contacts);
+ db_unset(0, MODULENAME, SHLExt_ShowNoProfile);
+ db_unset(0, MODULENAME, SHLExt_ShowNoIcons);
+ db_unset(0, MODULENAME, SHLExt_ShowNoOffline);
+
+ CheckUnregisterServer();
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, 0);
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int OnOptionsInit(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE opt = { sizeof(opt) };
+ opt.flags = ODPF_BOLDGROUPS;
+ opt.pszGroup = "Plugins";
+ opt.position = -1066;
+ opt.pszTitle = "Shell context menus";
+ opt.pszTemplate = MAKEINTRESOURCEA(IDD_SHLOPTS);
+ opt.hInstance = hInst;
+ opt.pfnDlgProc = OptDialogProc;
+ Options_AddPage(wParam, &opt);
+ return 0;
+}
diff --git a/plugins/ShellExt/src/resource.h b/plugins/ShellExt/src/resource.h new file mode 100644 index 0000000000..4bf7a5d6d8 --- /dev/null +++ b/plugins/ShellExt/src/resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by autorun.rc
+//
+
+#define IDD_SHLOPTS 101
+
+#define IDC_USEGROUPS 1014
+#define IDC_CLISTGROUPS 1015
+#define IDC_SHOWFULL 1016
+#define IDC_NOPROF 1020
+#define IDC_SHOWINVISIBLES 1021
+#define IDC_HIDEOFFLINE 1022
+#define IDC_STATUS 1023
+#define IDC_CAPMENUS 1025
+#define IDC_CAPSTATUS 1026
+#define IDC_CAPSHLSTATUS 1027
+#define IDC_REMOVE 1028
+#define IDC_USEOWNERDRAW 1029
diff --git a/plugins/ShellExt/src/shlcom.cpp b/plugins/ShellExt/src/shlcom.cpp new file mode 100644 index 0000000000..f2f06f3d0b --- /dev/null +++ b/plugins/ShellExt/src/shlcom.cpp @@ -0,0 +1,2122 @@ +#include "stdafx.h"
+#include "shlcom.h"
+
+struct dllpublic
+{
+ int FactoryCount, ObjectCount;
+};
+
+bool VistaOrLater;
+
+#define IPC_PACKET_SIZE (0x1000 * 32)
+#define IPC_PACKET_NAME "m.mi.miranda.ipc.server"
+
+struct TCMInvokeCommandInfo
+{
+ int cbSize;
+ DWORD fMask;
+ HWND hwnd;
+ char *lpVerb; // maybe index, type cast as Integer
+ char *lpParams;
+ char *lpDir;
+ int nShow;
+ DWORD dwHotkey;
+ HICON hIcon;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int IsCOMRegistered()
+{
+ HKEY hRegKey;
+ int res = 0;
+
+ // these arent the BEST checks in the world
+ if ( !RegOpenKeyExA(HKEY_CLASSES_ROOT, "miranda.shlext", 0, KEY_READ, &hRegKey)) {
+ res += COMREG_OK;
+ RegCloseKey(hRegKey);
+ }
+
+ if ( !RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 0, KEY_READ, &hRegKey)) {
+ DWORD lpType = REG_SZ;
+ if ( !RegQueryValueExA(hRegKey, "{72013A26-A94C-11d6-8540-A5E62932711D}", NULL, &lpType, 0, 0))
+ res += COMREG_APPROVED;
+ RegCloseKey(hRegKey);
+ }
+
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static string CreateProcessUID(int pid)
+{
+ char buf[100];
+ mir_snprintf(buf, sizeof(buf), "mim.shlext.%d$", pid);
+ return buf;
+}
+
+static string CreateUID()
+{
+ char buf[100];
+ mir_snprintf(buf, sizeof(buf), "'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++;
+
+ mir_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 '\' && 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 | at most two levels of the tree
+ // per tokenised section, && 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 = mir_hashstr(sz);
+ // find this node within
+ while (pg != NULL) {
+ // does this node have the right hash && 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
+ // | 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 && 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 = mir_hashstr(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 && we have created the first node
+ // which maybe Miranda, thus giving the wrong hash
+ // since "Miranda" can be a group of it's own && 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 && 'clear list' menu
+ mii.fType = MFT_SEPARATOR;
+ mii.fMask = MIIM_TYPE;
+ InsertMenuItemA(lParam->Self->hRecentMenu, 0xFFFFFFFF, true, &mii);
+
+ // insert 'clear MRU' item && 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 && 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 | 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)
+{
+ TSlotIPC *pct;
+ TShlComRec *Self;
+ UINT j;
+ IImageFactory *imageFactory;
+
+ pct = lParam->ipch->NewIconsBegin;
+ 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*)mir_realloc(Self->ProtoIcons, (Self->ProtoIconsCount + 1) * sizeof(TSlotProtoIcons));
+ TSlotProtoIcons *d = &Self->ProtoIcons[Self->ProtoIconsCount];
+ memmove(d, p, sizeof(TSlotProtoIcons));
+
+ // if using Vista (| later), clone all the icons into bitmaps && keep these around,
+ // if using anything older, just use the default code, the bitmaps (&& | icons) will be freed
+ // with the shell object.
+
+ imageFactory = NULL;
+
+ for (j = 0; j < 10; j++) {
+ if (imageFactory == NULL)
+ imageFactory = ARGB_GetWorker();
+ if (VistaOrLater) {
+ d->hBitmaps[j] = ARGB_BitmapFromIcon(imageFactory, Self->hMemDC, p->hIcons[j]);
+ d->hIcons[j] = 0;
+ }
+ else {
+ d->hBitmaps[j] = 0;
+ d->hIcons[j] = CopyIcon(p->hIcons[j]);
+ }
+ }
+
+ if (imageFactory != NULL) {
+ imageFactory->ptrVTable->Release(imageFactory);
+ imageFactory = NULL;
+ }
+
+ Self->ProtoIconsCount++;
+ pct = pct->Next;
+ }
+}
+
+BOOL __stdcall ProcessRequest(HWND hwnd, TEnumData *lParam)
+{
+ HANDLE hMirandaWorkEvent;
+ int replyBits;
+ char szBuf[MAX_PATH];
+
+ BOOL Result = true;
+ DWORD pid = 0;
+ GetWindowThreadProcessId(hwnd, &pid);
+ if (pid != 0) {
+ // old system would get a window's pid && the module handle that created it
+ // && 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).c_str());
+ if (hMirandaWorkEvent != 0) {
+ GetClassNameA(hwnd, szBuf, sizeof(szBuf));
+ if ( lstrcmpA(szBuf, MirandaName) != 0) {
+ // opened but not valid.
+ CloseHandle(hMirandaWorkEvent);
+ return Result;
+ }
+ }
+ // if the event object exists, a shlext.dll running in the instance must of created it.
+ if (hMirandaWorkEvent != 0) {
+ // 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, | it'll be the request
+ // bits as sent | 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) {
+ // 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 && 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);
+ }
+ }
+}
+
+function TShlComRec_QueryInterface(Self: PCommon_Interface; const IID: TIID; Obj): HResult; stdcall;
+{
+ Pointer(Obj) = NULL;
+ { IShellExtInit is given when the TShlRec is created }
+ if IsEqualIID(IID, IID_IContextMenu) | IsEqualIID(IID, IID_IContextMenu2) |
+ IsEqualIID(IID, IID_IContextMenu3)
+ {
+ with Self->ptrInstance^ do
+ {
+ Pointer(Obj) = @ContextMenu3_Interface;
+ inc(RefCount);
+ } { with }
+ Result = S_OK;
+ }
+ else
+ {
+ // 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 IsEqualIID(IID, IID_IShellExtInit)
+ {
+ with Self->ptrInstance^ do
+ {
+ Pointer(Obj) = @ShellExtInit_Interface;
+ inc(RefCount);
+ } // if
+ Result = S_OK;
+ }
+ else
+ {
+ Result = CLASS_E_CLASSNOTAVAILABLE;
+ } // if
+ } // if
+}
+
+function TShlComRec_AddRef(Self: PCommon_Interface): LongInt; stdcall;
+{
+ with Self->ptrInstance^ do
+ {
+ inc(RefCount);
+ Result = RefCount;
+ } { with }
+}
+
+function TShlComRec_Release(Self: PCommon_Interface): LongInt; stdcall;
+
+ j, c: Cardinal;
+{
+ with Self->ptrInstance^ do
+ {
+ dec(RefCount);
+ Result = RefCount;
+ if RefCount = 0
+ {
+ // time to go byebye.
+ with Self->ptrInstance^ do
+ {
+ // Note MRU menu is associated with a window (indirectly) so windows will free it.
+ // free icons!
+ if ProtoIcons != NULL
+ {
+ c = ProtoIconsCount;
+ while c > 0 do
+ {
+ dec(c);
+ for j = 0 to 9 do
+ {
+ with ProtoIcons[c] do
+ {
+ if hIcons[j] != 0
+ DestroyIcon(hIcons[j]);
+ if hBitmaps[j] != 0
+ DeleteObject(hBitmaps[j]);
+ }
+ }
+ }
+ FreeMem(ProtoIcons);
+ ProtoIcons = NULL;
+ } // if
+ // free IDataObject reference if pointer exists
+ if pDataObject != NULL
+ {
+ pDataObject->ptrVTable->Release(pDataObject);
+ } // if
+ pDataObject = NULL;
+ // free the heap && any memory allocated on it
+ HeapDestroy(hDllHeap);
+ // destroy the DC
+ if hMemDC != 0
+ DeleteDC(hMemDC);
+ } // with
+ // free the instance (class record) created
+ Dispose(Self->ptrInstance);
+ dec(dllpublic.ObjectCount);
+ } { if }
+ } { with }
+}
+
+function TShlComRec_Initialise(Self: PContextMenu3_Interface; pidLFolder: Pointer;
+ DObj: PDataObject_Interface; hKeyProdID: HKEY): HResult; stdcall;
+{
+ // 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.
+ with Self->ptrInstance^ do
+ {
+ if DObj != NULL
+ {
+ Result = S_OK;
+ // if an instance already exists, free it.
+ if pDataObject != NULL
+ pDataObject->ptrVTable->Release(pDataObject);
+ // store the new one && AddRef() it
+ pDataObject = DObj;
+ pDataObject->ptrVTable->AddRef(pDataObject);
+ }
+ else
+ {
+ Result = E_INVALIDARG;
+ } // if
+ } // if
+}
+
+function MAKE_HRESULT(Severity, Facility, Code: Integer): HResult;
+{$IFDEF FPC}
+inline;
+{$ENDIF}
+{
+ Result = (Severity shl 31) | (Facility shl 16) | Code;
+}
+
+function TShlComRec_QueryContextMenu(Self: PContextMenu3_Interface; Menu: HMENU;
+ indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
+type
+ TDllVersionInfo = record
+ cbSize: DWORD;
+ dwMajorVersion: DWORD;
+ dwMinorVersion: DWORD;
+ dwBuildNumber: DWORD;
+ dwPlatformID: DWORD;
+ }
+
+ TDllGetVersionProc = function( dv: TDllVersionInfo): HResult; stdcall;
+
+ hShellInst: HANDLE;
+ bMF_OWNERDRAW: Boolean;
+ DllGetVersionProc: TDllGetVersionProc;
+ dvi: TDllVersionInfo;
+ ed: TEnumData;
+ hMap: HANDLE;
+ pipch: THeaderIPC *;
+{
+ Result = 0;
+ if ((LOWORD(uFlags) && CMF_VERBSONLY) != CMF_VERBSONLY) &&
+ ((LOWORD(uFlags) && CMF_DEFAULTONLY) != CMF_DEFAULTONLY)
+ {
+ bMF_OWNERDRAW = false;
+ // get the shell version
+ hShellInst = LoadLibrary('shell32.dll');
+ if hShellInst != 0
+ {
+ DllGetVersionProc = GetProcAddress(hShellInst, 'DllGetVersion');
+ if @DllGetVersionProc != NULL
+ {
+ dvi.cbSize = sizeof(TDllVersionInfo);
+ if DllGetVersionProc(dvi) >= 0
+ {
+ // it's at least 4.00
+ bMF_OWNERDRAW = (dvi.dwMajorVersion > 4) | (dvi.dwMinorVersion >= 71);
+ } // if
+ } // if
+ FreeLibrary(hShellInst);
+ } // if
+
+ // if we're using Vista (| later), the ownerdraw code will be disabled, because the system draws the icons.
+ if VistaOrLater
+ bMF_OWNERDRAW = false;
+
+ hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, IPC_PACKET_SIZE,
+ IPC_PACKET_NAME);
+ if (hMap != 0) && (GetLastError != ERROR_ALREADY_EXISTS)
+ {
+ { map the memory to this address space }
+ pipch = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if pipch != NULL
+ {
+ { let the callback have instance vars }
+ ed.Self = Self->ptrInstance;
+ // not used 'ere
+ ed.Self->hRootMenu = Menu;
+ // store the first ID to offset with index for InvokeCommand()
+ Self->ptrInstance->idCmdFirst = idCmdFirst;
+ // store the starting index to offset
+ Result = idCmdFirst;
+ 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() }
+ lstrcpyA(@pipch->SignalEventName, LPSTR(CreateUID()));
+ { create the wait wait-for-wait object }
+ ed.hWaitFor = CreateEvent(NULL, false, false, pipch->SignalEventName);
+ if ed.hWaitFor != 0
+ {
+ { enumerate all the top level windows to find all loaded MIRANDANAME
+ classes -- }
+ EnumWindows(@ProcessRequest, lParam(@ed));
+ { close the wait-for-reply object }
+ CloseHandle(ed.hWaitFor);
+ }
+ { unmap the memory from this address space }
+ UnmapViewOfFile(pipch);
+ } { if }
+ { close the mapping }
+ CloseHandle(hMap);
+ // use the MSDN recommended way, thou there ain't much difference
+ Result = MAKE_HRESULT(0, 0, (ed.idCmdFirst - Result) + 1);
+ }
+ else
+ {
+ // the mapping file already exists, which is not good!
+ }
+ }
+ else
+ {
+ // same as giving a SEVERITY_SUCCESS, FACILITY_NULL, since that
+ // just clears the higher bits, which is done anyway
+ Result = MAKE_HRESULT(0, 0, 1);
+ } // if
+}
+
+function TShlComRec_GetCommandString(Self: PContextMenu3_Interface; idCmd, uType: UINT;
+ pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; stdcall;
+{
+ Result = E_NOTIMPL;
+}
+
+function ipcGetFiles(pipch: THeaderIPC *; pDataObject: PDataObject_Interface; const hContact: HANDLE): Integer;
+type
+ TDragQueryFile = function(hDrop: HANDLE; fileIndex: Integer; FileName: LPSTR;
+ cbSize: Integer): Integer; stdcall;
+
+ fet: TFormatEtc;
+ stgm: TStgMedium;
+ pct: TSlotIPC *;
+ iFile: Cardinal;
+ iFileMax: Cardinal;
+ hShell: HANDLE;
+ DragQueryFile: TDragQueryFile;
+ cbSize: Integer;
+ hDrop: HANDLE;
+{
+ Result = E_INVALIDARG;
+ hShell = LoadLibrary('shell32.dll');
+ if hShell != 0
+ {
+ DragQueryFile = GetProcAddress(hShell, 'DragQueryFileA');
+ if @DragQueryFile != NULL
+ {
+ fet.cfFormat = CF_HDROP;
+ fet.ptd = NULL;
+ fet.dwAspect = DVASPECT_CONTENT;
+ fet.lindex = -1;
+ fet.tymed = TYMED_HGLOBAL;
+ Result = pDataObject->ptrVTable->GetData(pDataObject, fet, stgm);
+ if Result = S_OK
+ {
+ // FIX, actually lock the global object && get a pointer
+ Pointer(hDrop) = GlobalLock(stgm.hGlobal);
+ if hDrop != 0
+ {
+ // get the maximum number of files
+ iFileMax = DragQueryFile(stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+ iFile = 0;
+ while iFile < iFileMax do
+ {
+ // get the size of the file path
+ cbSize = DragQueryFile(stgm.hGlobal, iFile, NULL, 0);
+ // get the buffer
+ pct = ipcAlloc(pipch, cbSize + 1); // including null term
+ // allocated?
+ if pct = NULL
+ break;
+ // store the hContact
+ pct->hContact = hContact;
+ // copy it to the buffer
+ DragQueryFile(stgm.hGlobal, iFile, LPSTR(UINT_PTR(pct) + sizeof(TSlotIPC)), pct->cbStrSection);
+ // next file
+ inc(iFile);
+ } // while
+ // store the number of files
+ pipch->Slots = iFile;
+ GlobalUnlock(stgm.hGlobal);
+ } // if hDrop check
+ // release the mediumn the lock may of failed
+ ReleaseStgMedium(stgm);
+ } // if
+ } // if
+ // free the dll
+ FreeLibrary(hShell);
+ } // if
+}
+
+function RequestTransfer(Self: PShlComRec; idxCmd: Integer): Integer;
+{
+ hMap: HANDLE;
+ pipch: THeaderIPC *;
+ mii: MENUITEMINFO;
+ hTransfer: HANDLE;
+ psd: PMenuDrawInfo;
+ hReply: HANDLE;
+ replyBits: Integer;
+
+ Result = E_INVALIDARG;
+ // get the contact information
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_ID | MIIM_DATA;
+ if GetMenuItemInfo(Self->hRootMenu, Self->idCmdFirst + idxCmd, false, mii)
+ {
+ // get the pointer
+ UINT_PTR(psd) = mii.dwItemData;
+ // the ID stored in the item pointer && the ID for the menu must match
+ if (psd = NULL) | (psd->wID != mii.wID)
+ {
+ // MessageBox(0,'ptr assocated with menu is NULL','',MB_OK);
+ Exit;
+ } // if
+ }
+ else
+ {
+ // MessageBox(0,'GetMenuItemInfo failed?','',MB_OK);
+ // couldn't get the info, can't start the transfer
+ Result = E_INVALIDARG;
+ Exit;
+ } // if
+ // is there an IDataObject instance?
+ if Self->pDataObject != NULL
+ {
+ // OpenEvent() the work object to see if the instance is still around
+ hTransfer = OpenEvent(EVENT_ALL_ACCESS, false, LPSTR(CreateProcessUID(psd->pid)));
+ if hTransfer != 0
+ {
+ // map the ipc file again
+ hMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,IPC_PACKET_SIZE,IPC_PACKET_NAME);
+ if (hMap != 0) && (GetLastError != ERROR_ALREADY_EXISTS)
+ {
+ // map it to process
+ pipch = 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, LPSTR(CreateUID()));
+ // create it
+ hReply = CreateEvent(NULL, false, false, pipch->SignalEventName);
+ if hReply != 0
+ {
+ if dtCommand in psd->fTypes
+ {
+ if Assigned(psd->MenuCommandCallback)
+ Result = 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
+ replyBits = ipcSendRequest(hTransfer, hReply, pipch, 200);
+ if replyBits != REPLY_FAIL
+ {
+ // they got the files!
+ Result = S_OK;
+ } // if
+ }
+
+ }
+ // close the work object name
+ CloseHandle(hReply);
+ } // if
+ // unmap it from this process
+ UnmapViewOfFile(pipch);
+ } // if
+ // close the map
+ CloseHandle(hMap);
+ } // if
+ // close the handle to the ST object name
+ CloseHandle(hTransfer);
+ } // if
+ } // if;
+}
+
+function TShlComRec_InvokeCommand(Self: PContextMenu3_Interface;
+ lpici: TCMInvokeCommandInfo): HResult; stdcall;
+{
+ Result = RequestTransfer(Self->ptrInstance, LOWORD(UINT_PTR(lpici.lpVerb)));
+}
+
+function TShlComRec_HandleMenuMsgs(Self: PContextMenu3_Interface; uMsg: UINT; wParam: wParam;
+ lParam: lParam; pResult: PLResult): HResult;
+const
+ WM_DRAWITEM = $002B;
+ WM_MEASUREITEM = $002C;
+
+ dwi: PDrawItemStruct;
+ msi: PMeasureItemStruct;
+ psd: PMenuDrawInfo;
+ ncm: TNonClientMetrics;
+ hOldFont: HANDLE;
+ hFont: HANDLE;
+ tS: TSize;
+ dx: Integer;
+ hBr: HBRUSH;
+ icorc: TRect;
+ hMemDC: HDC;
+{
+ pResult^ = Integer(true);
+ if (uMsg = WM_DRAWITEM) && (wParam = 0)
+ {
+ // either a main sub menu, a group menu | a contact
+ dwi = PDrawItemStruct(lParam);
+ UINT_PTR(psd) = dwi->itemData;
+ // don't fill
+ SetBkMode(dwi->HDC, TRANSPARENT);
+ // where to draw the icon?
+ icorc.Left = 0;
+ // center it
+ with dwi^ do
+ icorc.Top = rcItem.Top + ((rcItem.Bottom - rcItem.Top) div 2) - (16 div 2);
+ icorc.Right = icorc.Left + 16;
+ icorc.Bottom = icorc.Top + 16;
+ // draw for groups
+ if (dtGroup in psd->fTypes) | (dtEntry in psd->fTypes)
+ {
+ hBr = GetSysColorBrush(COLOR_MENU);
+ FillRect(dwi->HDC, dwi->rcItem, hBr);
+ DeleteObject(hBr);
+ //
+ if (ODS_SELECTED && 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));
+ } // if
+ // draw icon
+ with dwi^, icorc do
+ {
+ if (ODS_SELECTED && dwi->itemState) = ODS_SELECTED
+ {
+ hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
+ }
+ else
+ {
+ hBr = GetSysColorBrush(COLOR_MENU);
+ } // if
+ DrawIconEx(HDC, Left + 1, Top, psd->hStatusIcon, 16, 16, // width, height
+ 0, // step
+ hBr, // brush
+ DI_NORMAL);
+ DeleteObject(hBr);
+ } // with
+ // draw the text
+ with dwi^ do
+ {
+ inc(rcItem.Left, ((rcItem.Bottom - rcItem.Top) - 2));
+ DrawText(HDC, psd->szText, psd->cch, rcItem, DT_NOCLIP | DT_NOPREFIX |
+ DT_SINGLELINE | DT_VCENTER);
+ // draw the name of the database text if it's there
+ if psd->szProfile != NULL
+ {
+ GetTextExtentPoint32(dwi->HDC, psd->szText, psd->cch, tS);
+ inc(rcItem.Left, tS.cx + 8);
+ SetTextColor(HDC, GetSysColor(COLOR_GRAYTEXT));
+ DrawText(HDC, psd->szProfile, lstrlenA(psd->szProfile), rcItem,
+ DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+ } // if
+ } // with
+ }
+ else
+ {
+ // it's a contact!
+ hBr = GetSysColorBrush(COLOR_MENU);
+ FillRect(dwi->HDC, dwi->rcItem, hBr);
+ DeleteObject(hBr);
+ if ODS_SELECTED && dwi->itemState = ODS_SELECTED
+ {
+ hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
+ FillRect(dwi->HDC, dwi->rcItem, hBr);
+ DeleteObject(hBr);
+ SetTextColor(dwi->HDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ // draw icon
+ with dwi^, icorc do
+ {
+ if (ODS_SELECTED && dwi->itemState) = ODS_SELECTED
+ {
+ hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
+ }
+ else
+ {
+ hBr = GetSysColorBrush(COLOR_MENU);
+ } // if
+ DrawIconEx(HDC, Left + 2, Top, psd->hStatusIcon, 16, 16, // width, height
+ 0, // step
+ hBr, // brush
+ DI_NORMAL);
+ DeleteObject(hBr);
+ } // with
+ // draw the text
+ with dwi^ do
+ {
+ inc(rcItem.Left, (rcItem.Bottom - rcItem.Top) + 1);
+ DrawText(HDC, psd->szText, psd->cch, rcItem, DT_NOCLIP | DT_NOPREFIX |
+ DT_SINGLELINE | DT_VCENTER);
+ } // with
+ } // if
+ }
+ else if (uMsg = WM_MEASUREITEM)
+ {
+ // don't check if it's really a menu
+ msi = PMeasureItemStruct(lParam);
+ UINT_PTR(psd) = msi->itemData;
+ ncm.cbSize = sizeof(TNonClientMetrics);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @ncm, 0);
+ // create the font used in menus, this font should be cached somewhere really
+{$IFDEF FPC}
+ hFont = CreateFontIndirect(@ncm.lfMenuFont);
+{$ELSE}
+ hFont = CreateFontIndirect(ncm.lfMenuFont);
+{$ENDIF}
+ hMemDC = Self->ptrInstance->hMemDC;
+ // select in the font
+ hOldFont = SelectObject(hMemDC, hFont);
+ // default to an icon
+ dx = 16;
+ // get the size 'n' account for the icon
+ GetTextExtentPoint32(hMemDC, psd->szText, psd->cch, tS);
+ inc(dx, tS.cx);
+ // main menu item?
+ if psd->szProfile != NULL
+ {
+ GetTextExtentPoint32(hMemDC, psd->szProfile, lstrlenA(psd->szProfile), tS);
+ inc(dx, tS.cx);
+ }
+ // store it
+ msi->itemWidth = dx + Integer(ncm.iMenuWidth);
+ msi->itemHeight = Integer(ncm.iMenuHeight) + 2;
+ if tS.cy > msi->itemHeight
+ inc(msi->itemHeight, tS.cy - msi->itemHeight);
+ // clean up
+ SelectObject(hMemDC, hOldFont);
+ DeleteObject(hFont);
+ }
+ Result = S_OK;
+}
+
+function TShlComRec_HandleMenuMsg(Self: PContextMenu3_Interface; uMsg: UINT; wParam: wParam;
+ lParam: lParam): HResult; stdcall;
+
+ Dummy: HResult;
+{
+ Result = TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, @Dummy);
+}
+
+function TShlComRec_HandleMenuMsg2(Self: PContextMenu3_Interface; uMsg: UINT; wParam: wParam;
+ lParam: lParam; PLResult: Pointer { ^LResult } ): HResult; stdcall;
+
+ Dummy: HResult;
+{
+ // this will be null if a return value isn't needed.
+ if PLResult = NULL
+ PLResult = @Dummy;
+ Result = TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, PLResult);
+}
+
+function TShlComRec_Create: PShlComRec;
+
+ DC: HDC;
+{
+ New(Result);
+ { build all the function tables for interfaces }
+ with Result->ShellExtInit_Interface do
+ {
+ { this is only owned by us... }
+ ptrVTable = @vTable;
+ { IUnknown }
+ vTable.QueryInterface = @TShlComRec_QueryInterface;
+ vTable.AddRef = @TShlComRec_AddRef;
+ vTable.Release = @TShlComRec_Release;
+ { IShellExtInit }
+ vTable.Initialise = @TShlComRec_Initialise;
+ { instance of a TShlComRec }
+ ptrInstance = Result;
+ }
+ with Result->ContextMenu3_Interface do
+ {
+ ptrVTable = @vTable;
+ { IUnknown }
+ vTable.QueryInterface = @TShlComRec_QueryInterface;
+ vTable.AddRef = @TShlComRec_AddRef;
+ vTable.Release = @TShlComRec_Release;
+ { IContextMenu }
+ vTable.QueryContextMenu = @TShlComRec_QueryContextMenu;
+ vTable.InvokeCommand = @TShlComRec_InvokeCommand;
+ vTable.GetCommandString = @TShlComRec_GetCommandString;
+ { IContextMenu2 }
+ vTable.HandleMenuMsg = @TShlComRec_HandleMenuMsg;
+ { IContextMenu3 }
+ vTable.HandleMenuMsg2 = @TShlComRec_HandleMenuMsg2;
+ { instance data }
+ ptrInstance = Result;
+ }
+ { initalise variables }
+ Result->RefCount = 1;
+ Result->hDllHeap = HeapCreate(0, 0, 0);
+ Result->hRootMenu = 0;
+ Result->hRecentMenu = 0;
+ Result->RecentCount = 0;
+ Result->idCmdFirst = 0;
+ Result->pDataObject = NULL;
+ Result->ProtoIcons = NULL;
+ Result->ProtoIconsCount = 0;
+ // create an inmemory DC
+ DC = GetDC(0);
+ Result->hMemDC = CreateCompatibleDC(DC);
+ ReleaseDC(0, DC);
+ { keep count on the number of objects }
+ inc(dllpublic.ObjectCount);
+}
+
+{ IClassFactory }
+
+type
+
+ PVTable_IClassFactory = ^TVTable_IClassFactory;
+
+ TVTable_IClassFactory = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: Pointer;
+ Release: Pointer;
+ { IClassFactory }
+ CreateInstance: Pointer;
+ LockServer: Pointer;
+ }
+
+ PClassFactoryRec = ^TClassFactoryRec;
+
+ TClassFactoryRec = record
+ ptrVTable: PVTable_IClassFactory;
+ vTable: TVTable_IClassFactory;
+ { fields }
+ RefCount: LongInt;
+ }
+
+function TClassFactoryRec_QueryInterface(Self: PClassFactoryRec; const IID: TIID; Obj): HResult; stdcall;
+{
+ Pointer(Obj) = NULL;
+ Result = E_NOTIMPL;
+}
+
+function TClassFactoryRec_AddRef(Self: PClassFactoryRec): LongInt; stdcall;
+{
+ inc(Self->RefCount);
+ Result = Self->RefCount;
+}
+
+function TClassFactoryRec_Release(Self: PClassFactoryRec): LongInt; stdcall;
+{
+ dec(Self->RefCount);
+ Result = Self->RefCount;
+ if Result = 0
+ {
+ Dispose(Self);
+ dec(dllpublic.FactoryCount);
+ } { if }
+}
+
+function TClassFactoryRec_CreateInstance(Self: PClassFactoryRec; unkOuter: Pointer;
+ const IID: TIID; Obj): HResult; stdcall;
+
+ ShlComRec: PShlComRec;
+{
+ Pointer(Obj) = NULL;
+ Result = CLASS_E_NOAGGREGATION;
+ if unkOuter = NULL
+ {
+ { Before Vista, the system queried for a IShell interface queried for a context menu, Vista now
+ queries for a context menu (| a shell menu) QI()'s the other interface }
+ if IsEqualIID(IID, IID_IContextMenu)
+ {
+ Result = S_OK;
+ ShlComRec = TShlComRec_Create;
+ Pointer(Obj) = @ShlComRec->ContextMenu3_Interface;
+ }
+ if IsEqualIID(IID, IID_IShellExtInit)
+ {
+ Result = S_OK;
+ ShlComRec = TShlComRec_Create;
+ Pointer(Obj) = @ShlComRec->ShellExtInit_Interface;
+ } // if
+ } // if
+}
+
+function TClassFactoryRec_LockServer(Self: PClassFactoryRec; fLock: BOOL): HResult; stdcall;
+{
+ Result = E_NOTIMPL;
+}
+
+function TClassFactoryRec_Create: PClassFactoryRec;
+{
+ New(Result);
+ Result->ptrVTable = @Result->vTable;
+ { IUnknown }
+ Result->vTable.QueryInterface = @TClassFactoryRec_QueryInterface;
+ Result->vTable.AddRef = @TClassFactoryRec_AddRef;
+ Result->vTable.Release = @TClassFactoryRec_Release;
+ { IClassFactory }
+ Result->vTable.CreateInstance = @TClassFactoryRec_CreateInstance;
+ Result->vTable.LockServer = @TClassFactoryRec_LockServer;
+ { inital the variables }
+ Result->RefCount = 1;
+ { count the number of factories }
+ inc(dllpublic.FactoryCount);
+}
+
+//
+// IPC part
+//
+
+type
+ PFileList = ^TFileList;
+ TFileList = array [0 .. 0] of LPSTR;
+ PAddArgList = ^TAddArgList;
+
+ TAddArgList = record
+ szFile: LPSTR; // file being processed
+ cch: Cardinal; // it's length (with space for NULL char)
+ count: Cardinal; // number we have so far
+ files: PFileList;
+ hContact: HANDLE;
+ hEvent: HANDLE;
+ }
+
+function AddToList( args: TAddArgList): LongBool;
+
+ attr: Cardinal;
+ p: Pointer;
+ hFind: HANDLE;
+ fd: TWIN32FINDDATA;
+ szBuf: array [0 .. MAX_PATH] of Char;
+ szThis: LPSTR;
+ cchThis: Cardinal;
+{
+ Result = false;
+ attr = GetFileAttributes(args.szFile);
+ if (attr != 0xFFFFFFFF) && ((attr && FILE_ATTRIBUTE_HIDDEN) = 0)
+ {
+ if args.count mod 10 = 5
+ {
+ if CallService(MS_SYSTEM_TERMINATED, 0, 0) != 0
+ {
+ Result = true;
+ Exit;
+ } // if
+ }
+ if attr && FILE_ATTRIBUTE_DIRECTORY != 0
+ {
+ // add the directory
+ lstrcpyA(szBuf, args.szFile);
+ ReAllocMem(args.files, (args.count + 1) * sizeof(LPSTR));
+ GetMem(p, strlen(szBuf) + 1);
+ lstrcpyA(p, szBuf);
+ args.files^[args.count] = p;
+ inc(args.count);
+ // tack on ending search token
+ lstrcata(szBuf, '\*');
+ hFind = FindFirstFile(szBuf, fd);
+ while true do
+ {
+ if fd.cFileName[0] != '.'
+ {
+ lstrcpyA(szBuf, args.szFile);
+ lstrcata(szBuf, '\');
+ lstrcata(szBuf, fd.cFileName);
+ // keep a copy of the current thing being processed
+ szThis = args.szFile;
+ args.szFile = szBuf;
+ cchThis = args.cch;
+ args.cch = strlen(szBuf) + 1;
+ // recurse
+ Result = AddToList(args);
+ // restore
+ args.szFile = szThis;
+ args.cch = cchThis;
+ if Result
+ break;
+ } // if
+ if not FindNextFile(hFind, fd)
+ break;
+ } // while
+ FindClose(hFind);
+ }
+ else
+ {
+ // add the file
+ ReAllocMem(args.files, (args.count + 1) * sizeof(LPSTR));
+ GetMem(p, args.cch);
+ lstrcpyA(p, args.szFile);
+ args.files^[args.count] = p;
+ inc(args.count);
+ } // if
+ }
+}
+
+procedure MainThreadIssueTransfer(p: PAddArgList); stdcall;
+{$DEFINE SHL_IDC}
+{$DEFINE SHL_KEYS}
+{$INCLUDE shlc.inc}
+{$UNDEF SHL_KEYS}
+{$UNDEF SHL_IDC}
+{
+ DBWriteContactSettingByte(p->hContact, SHLExt_Name, SHLExt_MRU, 1);
+ CallService(MS_FILE_SENDSPECIFICFILES, p->hContact, lParam(p->files));
+ SetEvent(p->hEvent);
+}
+
+procedure IssueTransferThread(pipch: THeaderIPC *); cdecl;
+
+ szBuf: array [0 .. MAX_PATH] of Char;
+ pct: TSlotIPC *;
+ args: TAddArgList;
+ bQuit: LongBool;
+ j, c: Cardinal;
+ p: Pointer;
+ hMainThread: HANDLE;
+{
+ hMainThread = HANDLE(pipch->Param);
+ GetCurrentDirectory(sizeof(szBuf), szBuf);
+ args.count = 0;
+ args.files = NULL;
+ pct = pipch->DataPtr;
+ bQuit = false;
+ while pct != NULL do
+ {
+ if (pct->cbSize != sizeof(TSlotIPC))
+ break;
+ args.szFile = LPSTR(UINT_PTR(pct) + sizeof(TSlotIPC));
+ args.hContact = pct->hContact;
+ args.cch = pct->cbStrSection + 1;
+ bQuit = AddToList(args);
+ if bQuit
+ break;
+ pct = pct->Next;
+ } // while
+ if args.files != NULL
+ {
+ ReAllocMem(args.files, (args.count + 1) * sizeof(LPSTR));
+ args.files^[args.count] = NULL;
+ inc(args.count);
+ if (not bQuit)
+ {
+ args.hEvent = CreateEvent(NULL, true, false, NULL);
+ QueueUserAPC(@MainThreadIssueTransfer, hMainThread, UINT_PTR(@args));
+ while true do
+ {
+ if WaitForSingleObjectEx(args.hEvent, INFINITE, true) != WAIT_IO_COMPLETION
+ break;
+ }
+ CloseHandle(args.hEvent);
+ } // if
+ c = args.count - 1;
+ for j = 0 to c do
+ {
+ p = args.files^[j];
+ if p != NULL
+ FreeMem(p);
+ }
+ FreeMem(args.files);
+ }
+ SetCurrentDirectory(szBuf);
+ FreeMem(pipch);
+ CloseHandle(hMainThread);
+}
+
+type
+
+ PSlotInfo = ^TSlotInfo;
+
+ TSlotInfo = record
+ hContact: HANDLE;
+ hProto: Cardinal;
+ dwStatus: Integer; // will be aligned anyway
+ }
+
+ TSlotArray = array [0 .. $FFFFFF] of TSlotInfo;
+ PSlotArray = ^TSlotArray;
+
+function SortContact( Item1, Item2: TSlotInfo): Integer; stdcall;
+{
+ Result = CallService(MS_CLIST_CONTACTSCOMPARE, Item1.hContact, Item2.hContact);
+}
+
+// from FP FCL
+
+procedure QuickSort(FList: PSlotArray; L, R: LongInt);
+
+ i, j: LongInt;
+ p, q: TSlotInfo;
+{
+ repeat
+ i = L;
+ j = R;
+ p = FList^[(L + R) div 2];
+ repeat
+ while SortContact(p, FList^[i]) > 0 do
+ inc(i);
+ while SortContact(p, FList^[j]) < 0 do
+ dec(j);
+ if i <= j
+ {
+ q = FList^[i];
+ FList^[i] = FList^[j];
+ FList^[j] = q;
+ inc(i);
+ dec(j);
+ } // if
+ until i > j;
+ if L < j
+ QuickSort(FList, L, j);
+ L = i;
+ until i >= R;
+}
+
+{$DEFINE SHL_KEYS}
+{$INCLUDE shlc.inc}
+{$UNDEF SHL_KEYS}
+
+procedure ipcGetSkinIcons(ipch: THeaderIPC *);
+
+ protoCount: Integer;
+ pp: ^PPROTOCOLDESCRIPTOR;
+ spi: TSlotProtoIcons;
+ j: Cardinal;
+ pct: TSlotIPC *;
+ szTmp: array [0 .. 63] of Char;
+ dwCaps: Cardinal;
+{
+ if (CallService(MS_PROTO_ENUMACCOUNTS, wParam(@protoCount), lParam(@pp)) = 0) &&
+ (protoCount != 0)
+ {
+ spi.pid = GetCurrentProcessId();
+ while protoCount > 0 do
+ {
+ lstrcpyA(szTmp, pp->szName);
+ lstrcata(szTmp, PS_GETCAPS);
+ dwCaps = CallService(szTmp, PFLAGNUM_1, 0);
+ if (dwCaps && PF1_FILESEND) != 0
+ {
+ pct = ipcAlloc(ipch, sizeof(TSlotProtoIcons));
+ if pct != NULL
+ {
+ // capture all the icons!
+ spi.hProto = StrHash(pp->szName);
+ for j = 0 to 9 do
+ {
+ spi.hIcons[j] = LoadSkinnedProtoIcon(pp->szName, ID_STATUS_OFFLINE + j);
+ } // for
+ pct->fType = REQUEST_NEWICONS;
+ CopyMemory(Pointer(UINT_PTR(pct) + sizeof(TSlotIPC)), @spi, sizeof(TSlotProtoIcons));
+ if ipch->NewIconsBegin = NULL
+ ipch->NewIconsBegin = pct;
+ } // if
+ } // if
+ inc(pp);
+ dec(protoCount);
+ } // while
+ } // if
+ // add Miranda icon
+ pct = ipcAlloc(ipch, sizeof(TSlotProtoIcons));
+ if pct != NULL
+ {
+ ZeroMemory(@spi.hIcons, sizeof(spi.hIcons));
+ spi.hProto = 0; // no protocol
+ spi.hIcons[0] = LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ pct->fType = REQUEST_NEWICONS;
+ CopyMemory(Pointer(UINT_PTR(pct) + sizeof(TSlotIPC)), @spi, sizeof(TSlotProtoIcons));
+ if ipch->NewIconsBegin = NULL
+ ipch->NewIconsBegin = pct;
+ } // if
+}
+
+function ipcGetSortedContacts(ipch: THeaderIPC *; pSlot: pint; bGroupMode: Boolean): Boolean;
+
+ dwContacts: Cardinal;
+ pContacts: PSlotArray;
+ hContact: HANDLE;
+ i: Integer;
+ dwOnline: Cardinal;
+ szProto: LPSTR;
+ dwStatus: Integer;
+ pct: TSlotIPC *;
+ szContact: LPSTR;
+ dbv: TDBVariant;
+ bHideOffline: Boolean;
+ szTmp: array [0 .. 63] of Char;
+ dwCaps: Cardinal;
+ szSlot: LPSTR;
+ n, rc, cch: Cardinal;
+{
+ Result = false;
+ // hide offliners?
+ bHideOffline = DBGetContactSettingByte(0, 'CList', 'HideOffline', 0) = 1;
+ // do they wanna hide the offline people anyway?
+ if DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoOffline, 0) = 1
+ {
+ // hide offline people
+ bHideOffline = true;
+ }
+ // get the number of contacts
+ dwContacts = CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);
+ if dwContacts = 0
+ Exit;
+ // get the contacts in the array to be sorted by status, trim out anyone
+ // who doesn't wanna be seen.
+ GetMem(pContacts, (dwContacts + 2) * sizeof(TSlotInfo));
+ i = 0;
+ dwOnline = 0;
+ hContact = db_find_first();
+ while (hContact != 0) do
+ {
+ if i >= dwContacts
+ break;
+ (* do they have a running protocol? *)
+ UINT_PTR(szProto) = CallService(MS_PROTO_GETCONTACTBASEPROTO, hContact, 0);
+ if szProto != NULL
+ {
+ (* does it support file sends? *)
+ lstrcpyA(szTmp, szProto);
+ lstrcata(szTmp, PS_GETCAPS);
+ dwCaps = CallService(szTmp, PFLAGNUM_1, 0);
+ if (dwCaps && PF1_FILESEND) = 0
+ {
+ hContact = db_find_next(hContact);
+ continue;
+ }
+ dwStatus = DBGetContactSettingWord(hContact, szProto, 'Status', ID_STATUS_OFFLINE);
+ if dwStatus != ID_STATUS_OFFLINE
+ inc(dwOnline)
+ else if bHideOffline
+ {
+ hContact = db_find_next(hContact);
+ continue;
+ } // if
+ // is HIT on?
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHITContacts,
+ BST_UNCHECKED)
+ {
+ // don't show people who are "Hidden" "NotOnList" | Ignored
+ if (DBGetContactSettingByte(hContact, 'CList', 'Hidden', 0) = 1) |
+ (DBGetContactSettingByte(hContact, 'CList', 'NotOnList', 0) = 1) |
+ (CallService(MS_IGNORE_ISIGNORED, hContact, IGNOREEVENT_MESSAGE |
+ IGNOREEVENT_URL | IGNOREEVENT_FILE) != 0)
+ {
+ hContact = db_find_next(hContact);
+ continue;
+ } // if
+ } // if
+ // is HIT2 off?
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHIT2Contacts,
+ BST_UNCHECKED)
+ {
+ if DBGetContactSettingWord(hContact, szProto, 'ApparentMode', 0) = ID_STATUS_OFFLINE
+
+ {
+ hContact = db_find_next(hContact);
+ continue;
+ } // if
+ } // if
+ // store
+ pContacts^[i].hContact = hContact;
+ pContacts^[i].dwStatus = dwStatus;
+ pContacts^[i].hProto = StrHash(szProto);
+ inc(i);
+ }
+ else
+ {
+ // contact has no protocol!
+ } // if
+ hContact = db_find_next(hContact);
+ } // while
+ // if no one is online && the CList isn't showing offliners, quit
+ if (dwOnline = 0) && (bHideOffline)
+ {
+ FreeMem(pContacts);
+ Exit;
+ } // if
+ dwContacts = i;
+ i = 0;
+ // sort the array
+ QuickSort(pContacts, 0, dwContacts - 1);
+ // create an IPC slot for each contact && store display name, etc
+ while i < dwContacts do
+ {
+ UINT_PTR(szContact) = CallService(MS_CLIST_GETCONTACTDISPLAYNAME,pContacts^[i].hContact, 0);
+ if (szContact != NULL)
+ {
+ n = 0;
+ rc = 1;
+ if bGroupMode
+ {
+ rc = DBGetContactSetting(pContacts^[i].hContact, 'CList', 'Group', @dbv);
+ if rc = 0
+ {
+ n = lstrlenA(dbv.szVal.a) + 1;
+ }
+ } // if
+ cch = lstrlenA(szContact) + 1;
+ pct = ipcAlloc(ipch, cch + 1 + n);
+ if pct = NULL
+ {
+ DBFreeVariant(@dbv);
+ break;
+ }
+ // lie about the actual size of the TSlotIPC
+ pct->cbStrSection = cch;
+ szSlot = LPSTR(UINT_PTR(pct) + sizeof(TSlotIPC));
+ lstrcpyA(szSlot, szContact);
+ pct->fType = REQUEST_CONTACTS;
+ pct->hContact = pContacts^[i].hContact;
+ pct->Status = pContacts^[i].dwStatus;
+ pct->hProto = pContacts^[i].hProto;
+ pct->MRU = DBGetContactSettingByte(pct->hContact, SHLExt_Name, SHLExt_MRU, 0);
+ if ipch->ContactsBegin = NULL
+ ipch->ContactsBegin = pct;
+ inc(szSlot, cch + 1);
+ if rc = 0
+ {
+ pct->hGroup = StrHash(dbv.szVal.a);
+ lstrcpyA(szSlot, dbv.szVal.a);
+ DBFreeVariant(@dbv);
+ }
+ else
+ {
+ pct->hGroup = 0;
+ szSlot^ = #0;
+ }
+ inc(pSlot^);
+ } // if
+ inc(i);
+ } // while
+ FreeMem(pContacts);
+ //
+ Result = true;
+}
+
+// worker thread to clear MRU, called by the IPC bridge
+procedure ClearMRUThread(notused: Pointer); cdecl;
+{$DEFINE SHL_IDC}
+{$DEFINE SHL_KEYS}
+{$INCLUDE shlc.inc}
+{$UNDEF SHL_KEYS}
+{$UNDEF SHL_IDC}
+
+ hContact: HANDLE;
+{
+ {
+ hContact = db_find_first();
+ while hContact != 0 do
+ {
+ if DBGetContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0) > 0
+ {
+ DBWriteContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0);
+ }
+ hContact = db_find_next(hContact);
+ }
+ }
+}
+
+// this function is called from an APC into the main thread
+procedure ipcService(dwParam: DWORD); stdcall;
+label
+ Reply;
+
+ hMap: HANDLE;
+ pMMT: THeaderIPC *;
+ hSignal: HANDLE;
+ pct: TSlotIPC *;
+ szBuf: LPSTR;
+ iSlot: Integer;
+ szGroupStr: array [0 .. 31] of Char;
+ dbv: TDBVariant;
+ bits: pint;
+ bGroupMode: Boolean;
+ cloned: THeaderIPC *;
+ szMiranda: LPSTR;
+{
+ { try to open the file mapping object the caller must make sure no other
+ running instance is using this file }
+ hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, IPC_PACKET_NAME);
+ if hMap != 0
+ {
+ { map the file to this process }
+ pMMT = 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
+ bits = @pMMT->fRequests;
+ // jump right to a worker thread for file processing?
+ if (bits^ && REQUEST_XFRFILES) = REQUEST_XFRFILES
+ {
+ GetMem(cloned, 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) = 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 DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoIcons, 0) != 0
+ {
+ pMMT->dwFlags = HIPC_NOICONS;
+ }
+ // see if we have a custom string for 'Miranda'
+ szMiranda = Translate('Miranda');
+ lstrcpyn(pMMT->MirandaName, szMiranda, sizeof(pMMT->MirandaName) - 1);
+
+ // for the MRU menu
+ szBuf = Translate('Recently');
+ lstrcpyn(pMMT->MRUMenuName, szBuf, sizeof(pMMT->MRUMenuName) - 1);
+
+ // && a custom string for "clear entries"
+ szBuf = Translate('Clear entries');
+ lstrcpyn(pMMT->ClearEntries, szBuf, sizeof(pMMT->ClearEntries) - 1);
+
+ // if the group mode is on, check if they want the CList setting
+ bGroupMode = BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseGroups,
+ BST_UNCHECKED);
+ if bGroupMode && (BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name,
+ SHLExt_UseCListSetting, BST_UNCHECKED))
+ {
+ bGroupMode = 1 = DBGetContactSettingByte(0, 'CList', 'UseGroups', 0);
+ }
+ iSlot = 0;
+ // return profile if set
+ if BST_UNCHECKED = DBGetContactSettingByte(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
+ } // if
+ if (bits^ && REQUEST_NEWICONS) = REQUEST_NEWICONS
+ {
+ ipcGetSkinIcons(pMMT);
+ }
+ if (bits^ && REQUEST_GROUPS = REQUEST_GROUPS)
+ {
+ // return contact's grouping if it's present
+ while bGroupMode do
+ {
+ str(iSlot, szGroupStr);
+ if DBGetContactSetting(0, 'CListGroups', szGroupStr, @dbv) != 0
+ break;
+ pct = ipcAlloc(pMMT, lstrlenA(dbv.szVal.a + 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;
+ UINT_PTR(szBuf) = UINT_PTR(pct) + sizeof(TSlotIPC); // get the } of the slot
+ lstrcpyA(szBuf, dbv.szVal.a + 1);
+ pct->hGroup = 0;
+ DBFreeVariant(@dbv); // free the string
+ }
+ else
+ {
+ // outta space
+ DBFreeVariant(@dbv);
+ break;
+ } // if
+ inc(iSlot);
+ } { while }
+ // if there was no space left, it'll } on null
+ if pct = NULL
+ bits^ = (bits^ | GROUPS_NOTIMPL) && not REQUEST_GROUPS;
+ } { if: group request }
+ // SHOULD check slot space.
+ if (bits^ && REQUEST_CONTACTS = REQUEST_CONTACTS)
+ {
+ if not ipcGetSortedContacts(pMMT, @iSlot, bGroupMode)
+ {
+ // fail if there were no contacts AT ALL
+ bits^ = (bits^ | CONTACTS_NOTIMPL) && not REQUEST_CONTACTS;
+ } // if
+ } // if:contact request
+ // store the number of slots allocated
+ pMMT->Slots = iSlot;
+ Reply:
+ { get the handle the caller wants to be signalled on }
+ hSignal = OpenEvent(EVENT_ALL_ACCESS, false, pMMT->SignalEventName);
+ { did it open? }
+ if hSignal != 0
+ {
+ { signal && close }
+ SetEvent(hSignal);
+ CloseHandle(hSignal);
+ }
+ { unmap the shared memory from this process }
+ UnmapViewOfFile(pMMT);
+ }
+ { close the map file }
+ CloseHandle(hMap);
+ } { if }
+ //
+}
+
+procedure ThreadServer(hMainThread: Pointer); cdecl;
+
+ hEvent: HANDLE;
+ retVal: Cardinal;
+{
+ hEvent = CreateEvent(NULL, false, false, LPSTR(CreateProcessUID(GetCurrentProcessId())));
+ while true do
+ {
+ retVal = WaitForSingleObjectEx(hEvent, INFINITE, true);
+ if retVal = WAIT_OBJECT_0
+ {
+ QueueUserAPC(@ipcService, HANDLE(hMainThread), 0);
+ } // if
+ if CallService(MS_SYSTEM_TERMINATED, 0, 0) = 1
+ break;
+ } // while
+ CloseHandle(hEvent);
+ CloseHandle(HANDLE(hMainThread));
+}
+
+procedure InvokeThreadServer;
+
+ hMainThread: HANDLE;
+{
+ hMainThread = 0;
+ DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), @hMainThread,
+ THREAD_SET_CONTEXT, false, 0);
+ if hMainThread != 0
+ mir_forkThread(@ThreadServer, Pointer(hMainThread));
+}
+
+{ exported functions }
+
+function DllGetClassObject(const CLSID: TCLSID; const IID: TIID; Obj): HResult; stdcall;
+{
+ Pointer(Obj) = NULL;
+ Result = CLASS_E_CLASSNOTAVAILABLE;
+ if (IsEqualCLSID(CLSID, CLSID_ISHLCOM)) && (IsEqualIID(IID, IID_IClassFactory)) &&
+ (FindWindow(MirandaName, NULL) != 0)
+ {
+ Pointer(Obj) = TClassFactoryRec_Create;
+ Result = S_OK;
+ } // if
+}
+
+function DllCanUnloadNow: HResult;
+{
+ if ((dllpublic.FactoryCount = 0) && (dllpublic.ObjectCount = 0))
+ {
+ Result = S_OK;
+ }
+ else
+ {
+ Result = S_FALSE;
+ } // if
+}
+
+{ helper functions }
+
+type
+
+ PSHELLEXECUTEINFO = ^TSHELLEXECUTEINFO;
+
+ TSHELLEXECUTEINFO = record
+ cbSize: DWORD;
+ fMask: LongInt;
+ hwnd: HANDLE;
+ lpVerb: LPSTR;
+ lpFile: LPSTR;
+ lpParameters: LPSTR;
+ lpDirectory: LPSTR;
+ nShow: Integer;
+ hInstApp: HANDLE;
+ lpIDLIst: Pointer;
+ lpClass: LPSTR;
+ HKEY: HANDLE;
+ dwHotkey: DWORD;
+ HICON: HANDLE; // is union
+ hProcess: HANDLE;
+ }
+
+function ShellExecuteEx( se: TSHELLEXECUTEINFO): Boolean; stdcall;
+ external 'shell32.dll' name 'ShellExecuteExA';
+
+function wsprintfs(lpOut, lpFmt: LPSTR; args: LPSTR): Integer; cdecl;
+ external 'user32.dll' name 'wsprintfA';
+
+function RemoveCOMRegistryEntries: HResult;
+
+ hRootKey: HKEY;
+{
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRootKey) = ERROR_SUCCESS
+
+ {
+ (* need to delete the subkey before the parent key is deleted under NT/2000/XP *)
+ RegDeleteKey(hRootKey, 'CLSID');
+ (* close the key *)
+ RegCloseKey(hRootKey);
+ (* delete it *)
+ if RegDeleteKey(HKEY_CLASSES_ROOT, 'miranda.shlext') != ERROR_SUCCESS
+ {
+ MessageBox(0,
+ 'Unable to delete registry key for "shlext COM", this key may already be deleted | you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ } // if
+ } // if
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, '\*\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS,
+ hRootKey) = ERROR_SUCCESS
+ {
+ if RegDeleteKey(hRootKey, 'miranda.shlext') != ERROR_SUCCESS
+ {
+ MessageBox(0,
+ 'Unable to delete registry key for "File context menu handlers", this key may already be deleted | you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ } // if
+ RegCloseKey(hRootKey);
+ } // if
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'Directory\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS,
+ hRootKey) = ERROR_SUCCESS
+ {
+ if RegDeleteKey(hRootKey, 'miranda.shlext') != ERROR_SUCCESS
+ {
+ MessageBox(0,
+ 'Unable to delete registry key for "Directory context menu handlers", this key may already be deleted | you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ } // if
+ RegCloseKey(hRootKey);
+ } // if
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ 'Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', 0, KEY_ALL_ACCESS,
+ hRootKey)
+ {
+ if RegDeleteValue(hRootKey, '{72013A26-A94C-11d6-8540-A5E62932711D}') != ERROR_SUCCESS
+ {
+ MessageBox(0,
+ 'Unable to delete registry entry for "Approved context menu handlers", this key may already be deleted | you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ } // if
+ RegCloseKey(hRootKey);
+ } // if
+ Result = S_OK;
+}
+
+{ called by the options code to remove COM entries, && before that, get permission, if required.
+}
+
+procedure CheckUnregisterServer;
+
+ sei: TSHELLEXECUTEINFO;
+ szBuf: array [0 .. MAX_PATH * 2] of Char;
+ szFileName: array [0 .. MAX_PATH] of Char;
+{
+ if not VistaOrLater
+ {
+ RemoveCOMRegistryEntries();
+ Exit;
+ }
+ // launches regsvr to remove the dll under admin.
+ GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName));
+ wsprintfs(szBuf, '/s /u "%s"', szFileName);
+ ZeroMemory(@sei, sizeof(sei));
+ sei.cbSize = sizeof(sei);
+ sei.lpVerb = 'runas';
+ sei.lpFile = 'regsvr32';
+ sei.lpParameters = szBuf;
+ ShellExecuteEx(sei);
+ Sleep(1000);
+ RemoveCOMRegistryEntries();
+}
+
+{ Wow, I can't believe there isn't a direct API for this - 'runas' will invoke the UAC && ask
+ for permission before installing the shell extension. note the filepath arg has to be quoted }
+procedure CheckRegisterServer;
+
+ hRegKey: HKEY;
+ sei: TSHELLEXECUTEINFO;
+ szBuf: array [0 .. MAX_PATH * 2] of Char;
+ szFileName: array [0 .. MAX_PATH] of Char;
+{
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRegKey)
+
+ {
+ RegCloseKey(hRegKey);
+ }
+ else
+ {
+ if VistaOrLater
+ {
+ MessageBox(0,
+ 'Shell context menus requires your permission to register with Windows Explorer (one time only).',
+ 'Miranda IM - Shell context menus (shlext.dll)', MB_OK | MB_ICONINFORMATION);
+ // /s = silent
+ GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName));
+ wsprintfs(szBuf, '/s "%s"', szFileName);
+ ZeroMemory(@sei, sizeof(sei));
+ sei.cbSize = sizeof(sei);
+ sei.lpVerb = 'runas';
+ sei.lpFile = 'regsvr32';
+ sei.lpParameters = szBuf;
+ ShellExecuteEx(sei);
+ }
+ }
+}
diff --git a/plugins/ShellExt/src/shlcom.h b/plugins/ShellExt/src/shlcom.h new file mode 100644 index 0000000000..5b701b4bf6 --- /dev/null +++ b/plugins/ShellExt/src/shlcom.h @@ -0,0 +1,193 @@ +
+#define REPLY_FAIL 0x88888888
+#define REPLY_OK 0x00000000
+
+#define REQUEST_ICONS 1
+#define REQUEST_GROUPS (REQUEST_ICONS << 1)
+#define REQUEST_CONTACTS (REQUEST_GROUPS << 1)
+#define REQUEST_XFRFILES (REQUEST_CONTACTS << 1)
+#define REQUEST_NEWICONS (REQUEST_XFRFILES << 1)
+#define REQUEST_CLEARMRU (REQUEST_NEWICONS << 1)
+
+#define ICONS_NOTIMPL 0x00000008
+#define GROUPS_NOTIMPL 0x00000080
+#define CONTACTS_NOTIMPL 0x00000800
+
+#define STATUS_PROFILENAME 2
+
+#define ICMF_NORMAL 0x00000000
+#define ICMF_DEFAULTONLY 0x00000001
+#define ICMF_VERBSONLY 0x00000002
+#define ICMF_EXPLORE 0x00000004
+#define ICMF_NOVERBS 0x00000008
+#define ICMF_CANRENAME 0x00000010
+#define ICMF_NODEFAULT 0x00000020
+#define ICMF_INCLUDESTATIC 0x00000040
+#define ICMF_RESERVED 0xFFFF0000
+
+// IContextMenu*:GetCommandString() uType flags
+
+#define IGCS_VERBA 0x00000000 // canonical verb
+#define IGCS_HELPTEXTA 0x00000001 // help text (for status bar)
+#define IGCS_VALIDATEA 0x00000002 // validate command exists
+#define IGCS_VERBW 0x00000004 // canonical verb (unicode)
+#define IGC_HELPTEXTW 0x00000005 // help text (unicode version)
+#define IGCS_VALIDATEW 0x00000006 // validate command exists (unicode)
+#define IGCS_UNICODE 0x00000004 // for bit testing - Unicode string
+#define IGCS_VERB GCS_VERBA
+#define IGCS_HELPTEXT GCS_HELPTEXTA
+#define IGCS_VALIDATE GCS_VALIDATEA
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TGroupNode
+{
+ TGroupNode *Left, *Right, *_prev, *_next;
+ int Depth;
+ UINT Hash; // hash of the group name alone
+ char *szGroup;
+ int cchGroup;
+ HMENU hMenu;
+ int hMenuGroupID;
+ DWORD dwItems;
+};
+
+struct TGroupNodeList
+{
+ TGroupNode *First, *Last;
+};
+
+struct TStrTokRec
+{
+ char *szStr, *szSet;
+ // need a delimiter after the token too?, e.g. FOO^BAR^ if FOO^BAR
+ // is the string then only FOO^ is returned, could cause infinite loops
+ // if the condition isn't accounted for thou.
+ bool bSetTerminator;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TSlotProtoIcons
+{
+ UINT pid; // pid of Miranda this protocol was on
+ UINT hProto; // hash of the protocol
+ HICON hIcons[10]; // each status in order of ID_STATUS_*
+ HBITMAP hBitmaps[10]; // each status "icon" as a bitmap
+};
+
+struct TSlotIPC
+{
+ BYTE cbSize;
+ int fType; // a REQUEST_* type
+ TSlotIPC *Next;
+ HANDLE hContact;
+ UINT hProto; // hash of the protocol the user is on
+ UINT hGroup; // hash of the entire path (not defined for REQUEST_GROUPS slots)
+ WORD Status;
+ // only used for contacts -- can be STATUS_PROFILENAME -- but that is because returning the profile name is optional
+ BYTE MRU; // if set, contact has been recently used
+ int cbStrSection;
+};
+
+struct THeaderIPC
+{
+ int cbSize;
+ DWORD dwVersion;
+ void *pServerBaseAddress, *pClientBaseAddress;
+ int fRequests;
+ DWORD dwFlags;
+ int Slots, Cardinal;
+ char SignalEventName[64];
+ char MirandaName[64];
+ char MRUMenuName[64];
+ char ClearEntries[64];
+ TSlotIPC *IconsBegin, *ContactsBegin, *GroupsBegin, *NewIconsBegin;
+ // start of an flat memory stack, which is referenced as a linked list
+ int DataSize;
+ TSlotIPC *DataPtr, *DataPtrEnd;
+ void *DataFramePtr;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TShlComRec
+{
+ IShellExtInit *ShellExtInit_Interface;
+ IContextMenu3 *ContextMenu3_Interface;
+
+ LONG RefCount;
+ // this is owned by the shell after items are added 'n' is used to
+ // grab menu information directly via id rather than array indexin'
+ HMENU hRootMenu;
+ int idCmdFirst;
+ // most of the memory allocated is on this heap object so HeapDestroy()
+ // can do most of the cleanup, extremely lazy I know.
+ HANDLE hDllHeap;
+ // This is a submenu that recently used contacts are inserted into
+ // the contact is inserted twice, once in its normal list (| group) && here
+ // Note: These variables are global data, but refered to locally by each instance
+ // Do not rely on these variables outside the process enumeration.
+ HMENU hRecentMenu;
+ ULONG RecentCount; // number of added items
+ // array of all the protocol icons, for every running instance!
+ TSlotProtoIcons *ProtoIcons;
+ int ProtoIconsCount;
+ // maybe null, taken from IShellExtInit_Initalise() && AddRef()'d
+ // only used if a Miranda instance is actually running && a user
+ // is selected
+ IDataObject *pDataObject;
+ // DC is used for font metrics && saves on creating && destroying lots of DC handles
+ // during WM_MEASUREITEM
+ HDC hMemDC;
+};
+
+struct TEnumData
+{
+ TShlComRec *Self;
+
+ // autodetected, don't hard code since shells that don't support it
+ // won't send WM_MEASUREITETM/WM_DRAWITEM at all.
+ BOOL bOwnerDrawSupported;
+ // as per user setting (maybe of multiple Mirandas)
+ BOOL bShouldOwnerDraw;
+ int idCmdFirst;
+ THeaderIPC *ipch;
+ // OpenEvent()'d handle to give each IPC server an object to set signalled
+ HANDLE hWaitFor;
+ DWORD pid; // sub-unique value used to make work object name
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+enum TSlotDrawType {dtEntry = 0x01, dtGroup = 0x02, dtContact = 0x04, dtCommand = 0x08 };
+typedef int TSlotDrawTypes;
+
+typedef int (__stdcall TMenuCommandCallback)(
+ 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
+ struct TMenuDrawInfo *psd); // command/draw info
+
+struct TMenuDrawInfo
+{
+ char *szText, *szProfile;
+ int cch;
+ int wID; // should be the same as the menu item's ID
+ TSlotDrawTypes fTypes;
+ HANDLE hContact;
+ HICON hStatusIcon; // HICON from Self->ProtoIcons[index].hIcons[status]; Do not DestroyIcon()
+ HBITMAP hStatusBitmap; // HBITMAP, don't free.
+ int pid;
+ TMenuCommandCallback *MenuCommandCallback; // dtCommand must be set also.
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void ipcPrepareRequests(int ipcPacketSize, THeaderIPC *pipch, DWORD fRequests);
+DWORD ipcSendRequest(HANDLE hSignal, HANDLE hWaitFor, THeaderIPC *pipch, DWORD dwTimeoutMsecs);
+TSlotIPC* ipcAlloc(THeaderIPC *pipch, int nSize);
+void ipcFixupAddresses(BOOL FromServer, THeaderIPC *pipch);
+
+TGroupNode* AllocGroupNode(TGroupNodeList *list, TGroupNode *Root, int Depth);
+TGroupNode* FindGroupNode(TGroupNode* P, const DWORD Hash, DWORD Depth);
diff --git a/plugins/ShellExt/src/stdafx.cpp b/plugins/ShellExt/src/stdafx.cpp new file mode 100644 index 0000000000..1ff112382c --- /dev/null +++ b/plugins/ShellExt/src/stdafx.cpp @@ -0,0 +1,18 @@ +/*
+Copyright (C) 2012-13 Miranda NG Project (http://miranda-ng.org)
+
+This program is free software; you can redistribute it &&/|
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY | 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
\ No newline at end of file diff --git a/plugins/ShellExt/src/stdafx.h b/plugins/ShellExt/src/stdafx.h new file mode 100644 index 0000000000..f0f47bbb70 --- /dev/null +++ b/plugins/ShellExt/src/stdafx.h @@ -0,0 +1,45 @@ +#define _CRT_SECURE_NO_WARNINGS
+
+#include <windows.h>
+#include <CommCtrl.h>
+#include <ShlObj.h>
+
+#include <string>
+
+#include <newpluginapi.h>
+
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_langpack.h>
+#include <m_options.h>
+#include <m_database.h>
+#include <win2k.h>
+
+#include "Version.h"
+
+using namespace std;
+
+#define MODULENAME "ShellExt"
+#define SHLExt_Name "shlext15"
+#define SHLExt_MRU "MRU"
+#define SHLExt_UseGroups "UseGroups"
+#define SHLExt_UseCListSetting "UseCLGroups"
+#define SHLExt_UseHITContacts "UseHITContacts"
+#define SHLExt_UseHIT2Contacts "UseHIT2Contacts"
+#define SHLExt_ShowNoProfile "ShowNoProfile"
+#define SHLExt_ShowNoIcons "ShowNoIcons"
+#define SHLExt_ShowNoOffline "ShowNoOffline"
+
+#define COMREG_UNKNOWN 0
+#define COMREG_OK 1
+#define COMREG_APPROVED 2
+
+void CheckRegisterServer();
+void CheckUnregisterServer();
+void InvokeThreadServer();
+int IsCOMRegistered();
+HRESULT RemoveCOMRegistryEntries();
+
+extern HINSTANCE hInst;
+
+int OnOptionsInit(WPARAM wParam, LPARAM lParam);
|