1163 lines
31 KiB
C++
1163 lines
31 KiB
C++
|
|
|
|
// Copyright (c) Microsoft Corporation
|
|
|
|
// File: openwith.cpp
|
|
|
|
// This file contains the implementation of COpenWithMenu, a context menu handler
|
|
|
|
// History:
|
|
// 2-27-98 by ningz
|
|
// 3-SEP-98 ZekeL - rewrote ExtAppList() and moved it here
|
|
|
|
#include "shellprv.h"
|
|
#include <fsmenu.h>
|
|
#include "ids.h"
|
|
#include <shlwapi.h>
|
|
#include "openwith.h"
|
|
#include "uemapp.h"
|
|
|
|
#define TF_OPENWITHMENU 0x00004000
|
|
|
|
|
|
#define SZOPENWITHLIST TEXT("OpenWithList")
|
|
#define REGSTR_PATH_EXPLORER_FILEEXTS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts")
|
|
#define _OpenWithListMaxItems() 10
|
|
|
|
typedef struct _OpenWithList {
|
|
HANDLE hMRU;
|
|
HANDLE hMutex;
|
|
HKEY hkLM;
|
|
HKEY hkCU;
|
|
} OPENWITHLIST, *POPENWITHLIST;
|
|
|
|
LONG _CreateListMutex(LPCTSTR pszExt, HANDLE *phMutex)
|
|
{
|
|
TCHAR szMutex[MAX_PATH];
|
|
|
|
wnsprintf(szMutex, SIZECHARS(szMutex), TEXT("%s%s"), SZOPENWITHLIST, pszExt);
|
|
CharLower(szMutex);
|
|
|
|
*phMutex = CreateMutex(CreateAllAccessSecurityAttributes(NULL, NULL, NULL), FALSE, szMutex);
|
|
|
|
if (!*phMutex)
|
|
return GetLastError();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
inline BOOL _GrabMutex(HANDLE hMutex)
|
|
{
|
|
DWORD dwResult = WaitForSingleObject(hMutex, INFINITE);
|
|
|
|
return (WAIT_OBJECT_0 == dwResult) ;
|
|
}
|
|
|
|
|
|
// OpenWithListClose()
|
|
// Frees up resources allocated by OpenWithListOpen(). No return value.
|
|
|
|
void OpenWithListClose(IN HANDLE hList)
|
|
{
|
|
POPENWITHLIST pList = (POPENWITHLIST) hList;
|
|
TraceMsg(TF_OPENWITHMENU, "[%X] OpenWithListClose() called", pList);
|
|
if (pList)
|
|
{
|
|
if (pList->hMutex)
|
|
CloseHandle(pList->hMutex);
|
|
|
|
if (pList->hkCU)
|
|
RegCloseKey(pList->hkCU);
|
|
|
|
if (pList->hkLM)
|
|
RegCloseKey(pList->hkLM);
|
|
|
|
if (pList->hMRU)
|
|
FreeMRUList(pList->hMRU);
|
|
|
|
LocalFree(pList);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// OpenWithListOpen()
|
|
// allocates and initializes the state for the openwithlist
|
|
|
|
HRESULT OpenWithListOpen(IN LPCTSTR pszExt, HANDLE *phList)
|
|
{
|
|
LONG lResult;
|
|
|
|
if (!pszExt || !*pszExt || !phList)
|
|
return E_INVALIDARG;
|
|
|
|
*phList = NULL;
|
|
|
|
POPENWITHLIST pList = (POPENWITHLIST) LocalAlloc(LPTR, sizeof(OPENWITHLIST));
|
|
|
|
if (!pList)
|
|
return E_OUTOFMEMORY;
|
|
|
|
lResult = _CreateListMutex(pszExt, &pList->hMutex);
|
|
|
|
if (NOERROR == lResult)
|
|
{
|
|
TCHAR szSubKey[MAX_PATH];
|
|
// Build up the subkey string.
|
|
wnsprintf(szSubKey, SIZECHARS(szSubKey), TEXT("%s\\%s\\%s"), REGSTR_PATH_EXPLORER_FILEEXTS, pszExt, SZOPENWITHLIST);
|
|
|
|
MRUINFO mi = {sizeof(mi), _OpenWithListMaxItems(), 0, HKEY_CURRENT_USER, szSubKey, NULL};
|
|
|
|
pList->hMRU = CreateMRUList(&mi);
|
|
|
|
// the MRU list initializes the key a specific way so that
|
|
// it is a real MRU...
|
|
if (pList->hMRU)
|
|
{
|
|
lResult = RegOpenKeyEx(HKEY_CURRENT_USER, szSubKey, 0L,
|
|
MAXIMUM_ALLOWED, &(pList->hkCU));
|
|
|
|
if (ERROR_SUCCESS == lResult)
|
|
{
|
|
// make different subkey for LM
|
|
wnsprintf(szSubKey, SIZECHARS(szSubKey), TEXT("%s\\%s"), pszExt, SZOPENWITHLIST);
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, szSubKey, 0L,
|
|
MAXIMUM_ALLOWED, &(pList->hkLM));
|
|
}
|
|
}
|
|
else // !pList->hMRU
|
|
lResult = ERROR_BADKEY;
|
|
}
|
|
|
|
if (NOERROR != lResult)
|
|
{
|
|
OpenWithListClose((HANDLE)pList);
|
|
TraceMsg(TF_OPENWITHMENU, "OpenWithListOpen() failed on %s, err = %d", pszExt, lResult);
|
|
return HRESULT_FROM_WIN32(lResult);
|
|
}
|
|
|
|
TraceMsg(TF_OPENWITHMENU, "[%X] OpenWithListOpen() created on %s", pList, pszExt);
|
|
*phList = (HANDLE) pList;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT _AddItem(HANDLE hMRU, HKEY hkList, LPCTSTR pszName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (hMRU)
|
|
{
|
|
int cItems = EnumMRUList(hMRU, -1, NULL, 0L);
|
|
|
|
// just trim us down to make room...
|
|
while (cItems >= _OpenWithListMaxItems())
|
|
DelMRUString(hMRU, --cItems);
|
|
|
|
if (0 > AddMRUString(hMRU, pszName))
|
|
hr = E_UNEXPECTED;
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void _DeleteItem(POPENWITHLIST pList, LPCTSTR pszName)
|
|
{
|
|
int iItem = FindMRUString(pList->hMRU, pszName, NULL);
|
|
if (0 <= iItem) {
|
|
DelMRUString(pList->hMRU, iItem);
|
|
}
|
|
|
|
}
|
|
|
|
STDAPI OpenWithListRegister(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszVerb, HKEY hkProgid)
|
|
{
|
|
if (!pszExt || !hkProgid)
|
|
return E_INVALIDARG;
|
|
|
|
POPENWITHLIST pList;
|
|
|
|
// -> Peruser entries are stored here
|
|
// HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts
|
|
// \.Ext
|
|
// Application = "foo.exe"
|
|
// \OpenWithList
|
|
// MRUList = "ab"
|
|
// a = "App.exe"
|
|
// b = "foo.exe"
|
|
|
|
// -> for permanent entries are stored un HKCR
|
|
// HKCR
|
|
// \.Ext
|
|
// \OpenWithList
|
|
// \app.exe
|
|
|
|
// -> and applications or the system can write app association here
|
|
// \Applications
|
|
// \APP.EXE
|
|
// \shell...
|
|
// \foo.exe
|
|
// \shell...
|
|
|
|
|
|
HRESULT hr = OpenWithListOpen(pszExt, (HANDLE *)&pList);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (_GrabMutex(pList->hMutex))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
HRESULT hr = AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, hkProgid, pszVerb, szPath, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szPath)));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPCTSTR pszExe = PathFindFileName(szPath);
|
|
|
|
if (IsPathInOpenWithKillList(pszExe))
|
|
hr = E_ACCESSDENIED;
|
|
else
|
|
hr = AssocMakeApplicationByKey(ASSOCMAKEF_VERIFY, hkProgid, pszVerb);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TraceMsg(TF_OPENWITHMENU, "[%X] OpenWithListRegister() adding %s",pList, pszExe);
|
|
hr = _AddItem(pList->hMRU, pList->hkCU, pszExe);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
_DeleteItem(pList, pszExe);
|
|
|
|
}
|
|
|
|
ReleaseMutex(pList->hMutex);
|
|
}
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
|
|
OpenWithListClose(pList);
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI_(void) OpenWithListSoftRegisterProcess(DWORD dwFlags, LPCTSTR pszExt)
|
|
{
|
|
if (!pszExt || !*pszExt)
|
|
return;
|
|
|
|
POPENWITHLIST pList;
|
|
|
|
if (SUCCEEDED(OpenWithListOpen(pszExt, (HANDLE *)&pList)))
|
|
{
|
|
if (_GrabMutex(pList->hMutex))
|
|
{
|
|
TCHAR szApp[MAX_PATH];
|
|
if (GetModuleFileName(NULL, szApp, SIZECHARS(szApp))
|
|
&& !IsPathInOpenWithKillList(szApp))
|
|
_AddItem(pList->hMRU, pList->hkCU, PathFindFileName(szApp));
|
|
|
|
ReleaseMutex(pList->hMutex);
|
|
}
|
|
|
|
OpenWithListClose(pList);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
typedef struct _OpenWithItem
|
|
{
|
|
LPTSTR pszFriendly;
|
|
LPTSTR pszVerb;
|
|
HKEY hKey;
|
|
} OPENWITHITEM, *POPENWITHITEM;
|
|
|
|
BOOL _GetAppKey(LPCTSTR pszApp, HKEY *phkApp)
|
|
{
|
|
ASSERT(pszApp && *pszApp);
|
|
TCHAR szKey[MAX_PATH];
|
|
lstrcpy(szKey, TEXT("Applications\\"));
|
|
StrCatBuff(szKey, pszApp, SIZECHARS(szKey));
|
|
|
|
return (NOERROR == RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0L, MAXIMUM_ALLOWED, phkApp));
|
|
}
|
|
|
|
DWORD _AppListEnumLM(HKEY hkey, HANDLE hMRU, LPCTSTR pszVerb, POPENWITHITEM pItems, ULONG cItems, DWORD index)
|
|
{
|
|
for (DWORD iItem = 0; index < cItems; iItem++)
|
|
{
|
|
TCHAR sz[MAX_PATH];
|
|
DWORD cch;
|
|
HKEY hkItem;
|
|
|
|
cch = SIZECHARS(sz);
|
|
|
|
if (NOERROR != RegEnumKeyEx(hkey, iItem, sz, &cch,
|
|
NULL, NULL, NULL, NULL))
|
|
break;
|
|
|
|
// if we already tried this from the MRU or
|
|
// if the APP key doesnt exist here, then
|
|
// just continue on to the next item
|
|
if ((0 <= FindMRUString(hMRU, sz, NULL))
|
|
|| IsPathInOpenWithKillList(sz)
|
|
|| !_GetAppKey(sz, &hkItem))
|
|
continue;
|
|
|
|
ASSERT(hkItem);
|
|
|
|
|
|
// this will filter out apps that dont support the specified verb
|
|
// we reuse sz here.
|
|
if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_FRIENDLYAPPNAME, hkItem, pszVerb, sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
|
|
{
|
|
pItems[index].pszFriendly = StrDup(sz);
|
|
pItems[index].pszVerb = pszVerb ? StrDup(pszVerb) : NULL;
|
|
pItems[index].hKey = hkItem;
|
|
hkItem = NULL;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
if (hkItem)
|
|
RegCloseKey(hkItem);
|
|
|
|
} // for
|
|
|
|
return index;
|
|
}
|
|
|
|
void _ReleaseListItem(POPENWITHITEM pItem)
|
|
{
|
|
if (pItem->pszFriendly)
|
|
LocalFree(pItem->pszFriendly);
|
|
|
|
if (pItem->pszVerb)
|
|
LocalFree(pItem->pszVerb);
|
|
|
|
if (pItem->hKey)
|
|
RegCloseKey(pItem->hKey);
|
|
|
|
// since this is called from only one place,
|
|
// we dont actually need to clear the values...
|
|
// ZeroMemory(pItem, SIZEOF(OPENWITHITEM))
|
|
}
|
|
|
|
|
|
// Must declare an explicit structure to keep Win64 happy.
|
|
// We actually hand out an internal pointer to the rgItems array,
|
|
// but occasionally we need to peek at the cItems.
|
|
|
|
typedef struct ITEMSLIST {
|
|
DWORD cItems;
|
|
OPENWITHITEM rgItems[1];
|
|
} ITEMSLIST, *PITEMSLIST;
|
|
|
|
void OpenWithListReleaseList(POPENWITHITEM pItems)
|
|
{
|
|
if (pItems)
|
|
{
|
|
PITEMSLIST pList = CONTAINING_RECORD(pItems, ITEMSLIST, rgItems);
|
|
|
|
DWORD cItems = pList->cItems;
|
|
for (DWORD i = 0; i < cItems; i++)
|
|
_ReleaseListItem(&pList->rgItems[i]);
|
|
LocalFree(pList);
|
|
}
|
|
}
|
|
|
|
DWORD _AllocList(POPENWITHLIST pList, POPENWITHITEM *ppItems)
|
|
{
|
|
|
|
DWORD cResult = (DWORD) EnumMRUList(pList->hMRU, -1, NULL, 0L);
|
|
|
|
if (cResult == (DWORD)-1)
|
|
cResult = 0;
|
|
|
|
if (pList->hkLM)
|
|
{
|
|
DWORD dw = 0;
|
|
RegQueryInfoKey(
|
|
pList->hkLM,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dw,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
cResult += dw;
|
|
}
|
|
|
|
|
|
if (cResult)
|
|
{
|
|
PITEMSLIST pList = (PITEMSLIST)LocalAlloc(LPTR, FIELD_OFFSET(ITEMSLIST, rgItems[cResult]));
|
|
|
|
if(pList)
|
|
*ppItems = &pList->rgItems[0];
|
|
else
|
|
cResult = 0;
|
|
}
|
|
return cResult;
|
|
}
|
|
|
|
DWORD _AppListEnumMRU(HANDLE hMRU, LPCTSTR pszVerb, POPENWITHITEM pItems, ULONG cItems, DWORD index)
|
|
{
|
|
for (int iItem = 0; index < cItems; iItem++)
|
|
{
|
|
TCHAR sz[MAX_PATH];
|
|
HKEY hkItem;
|
|
|
|
if (0 > EnumMRUList(hMRU, iItem, sz, SIZECHARS(sz)))
|
|
break;
|
|
|
|
// dont quit if we couldnt get this key...
|
|
if (IsPathInOpenWithKillList(sz) || !_GetAppKey(sz, &hkItem))
|
|
continue;
|
|
|
|
ASSERT(hkItem);
|
|
|
|
|
|
// this will filter out apps that dont support the specified verb
|
|
// we reuse sz here.
|
|
if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_FRIENDLYAPPNAME, hkItem, pszVerb, sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
|
|
{
|
|
pItems[index].pszFriendly = StrDup(sz);
|
|
pItems[index].pszVerb = pszVerb ? StrDup(pszVerb) : NULL;
|
|
pItems[index].hKey = hkItem;
|
|
hkItem = NULL;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
if (hkItem)
|
|
RegCloseKey(hkItem);
|
|
|
|
} // for
|
|
|
|
return index;
|
|
}
|
|
|
|
|
|
// OpenWithListGetList()
|
|
|
|
// Allocates and initializes a list of all the openwithlist items
|
|
// that are associated with the pszExt, and can shellexecute pszVerb.
|
|
// if the pszVerb is NULL, the default verbs are allowed
|
|
|
|
DWORD OpenWithListGetList(LPCTSTR pszExt, LPCTSTR pszVerb, POPENWITHITEM *ppItems)
|
|
{
|
|
POPENWITHLIST pList;
|
|
DWORD cResult = 0;
|
|
|
|
if (!pszExt || !ppItems)
|
|
return 0;
|
|
|
|
*ppItems = NULL;
|
|
|
|
if (SUCCEEDED(OpenWithListOpen(pszExt, (HANDLE *)&pList)))
|
|
{
|
|
if (_GrabMutex(pList->hMutex))
|
|
{
|
|
POPENWITHITEM pItems;
|
|
LONG cItems = _AllocList(pList, &pItems);
|
|
|
|
if (cItems)
|
|
{
|
|
// enum the MRU first, and then if there are
|
|
// any that were under HKCR, them we will add them too.
|
|
cResult = _AppListEnumMRU(pList->hMRU, pszVerb, pItems, cItems, 0);
|
|
if (pList->hkLM)
|
|
cResult =_AppListEnumLM(pList->hkLM, pList->hMRU, pszVerb, pItems, cItems, cResult);
|
|
|
|
if (cResult)
|
|
{
|
|
PITEMSLIST pList = CONTAINING_RECORD(pItems, ITEMSLIST, rgItems);
|
|
pList->cItems = cResult;
|
|
*ppItems = pItems;
|
|
}
|
|
else
|
|
{
|
|
OpenWithListReleaseList(pItems);
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseMutex(pList->hMutex);
|
|
}
|
|
|
|
OpenWithListClose((HANDLE)pList);
|
|
|
|
}
|
|
|
|
return cResult;
|
|
}
|
|
|
|
|
|
|
|
typedef struct
|
|
{
|
|
TCHAR szMenuText[MAX_PATH];
|
|
int iImage;
|
|
|
|
DWORD dwFlags;
|
|
} OPENWITHMENUITEMINFO, * LPOPENWITHMENUITEMINFO;
|
|
|
|
// format strings
|
|
const TCHAR c_szSShellSCommand[] = TEXT("%s\\Shell\\%s\\Command");
|
|
const TCHAR c_szSShell[] = TEXT("%s\\Shell");
|
|
|
|
class COpenWithMenu : public IContextMenu3, IShellExtInit,IObjectWithSite
|
|
{
|
|
// IUnknown
|
|
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
|
|
STDMETHOD_(ULONG,AddRef)(void);
|
|
STDMETHOD_(ULONG,Release)(void);
|
|
|
|
// IContextMenu
|
|
STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
|
|
STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
|
|
STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
|
|
|
|
// IContextMenu2
|
|
STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
// IContextMenu3
|
|
STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
|
|
|
|
// IShellExtInit
|
|
STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
|
|
|
|
//IObjectWithSite
|
|
STDMETHOD(SetSite)(IUnknown*);
|
|
STDMETHOD(GetSite)(REFIID,void**);
|
|
|
|
int _cRef;
|
|
HMENU _hMenu;
|
|
UINT _idCmdFirst;
|
|
int _nItems;
|
|
UINT _uFlags;
|
|
POPENWITHITEM _pItems;
|
|
TCHAR _szPath[MAX_PATH];
|
|
HIMAGELIST _himlSystemImageList;
|
|
IDataObject *_pdtobj;
|
|
IShellView2 *_pShellView2;
|
|
LPCITEMIDLIST _pidlFolder;
|
|
|
|
COpenWithMenu();
|
|
~COpenWithMenu();
|
|
|
|
friend HRESULT COpenWithMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID * ppvOut);
|
|
|
|
private:
|
|
//Handle Menu messages submitted to HandleMenuMsg
|
|
void DrawItem(DRAWITEMSTRUCT *lpdi);
|
|
LRESULT MeasureItem(MEASUREITEMSTRUCT *lpmi);
|
|
BOOL InitMenuPopup(HMENU hMenu);
|
|
|
|
//Internal Helpers
|
|
POPENWITHITEM GetItemData(HMENU hmenu, UINT iItem);
|
|
};
|
|
|
|
|
|
COpenWithMenu::COpenWithMenu() : _cRef(1)
|
|
{
|
|
TraceMsg(TF_OPENWITHMENU, "ctor COpenWithMenu %x", this);
|
|
}
|
|
|
|
COpenWithMenu::~COpenWithMenu()
|
|
{
|
|
TraceMsg(TF_OPENWITHMENU, "dtor COpenWithMenu %x", this);
|
|
|
|
if (_pdtobj)
|
|
_pdtobj->Release();
|
|
|
|
if (_pItems)
|
|
{
|
|
OpenWithListReleaseList(_pItems);
|
|
}
|
|
|
|
//Safety Net: Release my site in case I manage to get
|
|
// Released without my site SetSite(NULL) first.
|
|
ATOMICRELEASE(_pShellView2);
|
|
}
|
|
|
|
|
|
STDAPI COpenWithMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID * ppvOut)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
TraceMsg(TF_OPENWITHMENU, "COpenWithMenu_CreateInstance()");
|
|
*ppvOut = NULL;
|
|
|
|
if (pUnkOuter)
|
|
return CLASS_E_NOAGGREGATION;
|
|
|
|
COpenWithMenu * powm = new COpenWithMenu();
|
|
if (!powm)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = powm->QueryInterface(riid, (LPVOID *)ppvOut);
|
|
|
|
powm->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT COpenWithMenu::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_IContextMenu) ||
|
|
IsEqualIID(riid, IID_IContextMenu2) ||
|
|
IsEqualIID(riid, IID_IContextMenu3))
|
|
{
|
|
*ppvObj = SAFECAST(this, IContextMenu3 *);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IShellExtInit))
|
|
{
|
|
*ppvObj = SAFECAST(this, IShellExtInit *);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IObjectWithSite))
|
|
{
|
|
*ppvObj = SAFECAST(this, IObjectWithSite *);
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
ULONG COpenWithMenu::AddRef()
|
|
{
|
|
_cRef++;
|
|
TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::AddRef = %x", _cRef);
|
|
return _cRef;
|
|
}
|
|
|
|
ULONG COpenWithMenu::Release()
|
|
{
|
|
_cRef--;
|
|
TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::Release = %x", _cRef);
|
|
|
|
if (_cRef > 0)
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Purpose:
|
|
Add verb to extension app list
|
|
*/
|
|
VOID AddVerbItem(IQueryAssociations *pqa, HKEY hkeyClass, LPCTSTR pszExt, LPCTSTR pszVerb)
|
|
{
|
|
WCHAR wsz[MAX_PATH];
|
|
WCHAR wszVerb[MAX_PATH];
|
|
|
|
// HackHack: we don't want to put msohtmed.exe in openwithlist
|
|
if (pszVerb)
|
|
SHTCharToUnicode(pszVerb, wszVerb, SIZECHARS(wszVerb));
|
|
|
|
if (SUCCEEDED(pqa->GetString(0, ASSOCSTR_EXECUTABLE, pszVerb ? wszVerb : NULL, wsz,(LPDWORD)MAKEINTRESOURCE(SIZECHARS(wsz))))
|
|
&& (StrStrIW(wsz, L"msohtmed")))
|
|
return;
|
|
|
|
OpenWithListRegister(0, pszExt, pszVerb, hkeyClass);
|
|
}
|
|
|
|
/*
|
|
Purpose:
|
|
Add Open/Edit/Default verb to extension app list
|
|
*/
|
|
HRESULT AddVerbItems(LPCTSTR pszExt)
|
|
{
|
|
IQueryAssociations *pqa;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (SUCCEEDED(AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&pqa)))
|
|
{
|
|
HKEY hkeyClass;
|
|
WCHAR wszExt[MAX_PATH];
|
|
SHTCharToUnicode(pszExt, wszExt, SIZECHARS(wszExt));
|
|
|
|
if (SUCCEEDED(pqa->Init(0, wszExt, NULL, NULL))
|
|
&& (SUCCEEDED(pqa->GetKey(0, ASSOCKEY_SHELLEXECCLASS, NULL, &hkeyClass))))
|
|
{
|
|
AddVerbItem(pqa, hkeyClass, pszExt, NULL);
|
|
AddVerbItem(pqa, hkeyClass, pszExt, c_szOpen);
|
|
AddVerbItem(pqa, hkeyClass, pszExt, c_szEdit);
|
|
RegCloseKey(hkeyClass);
|
|
hr = S_OK;
|
|
}
|
|
pqa->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT COpenWithMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
|
{
|
|
MENUITEMINFO mii;
|
|
LPTSTR pszExt;
|
|
TCHAR szOpenWithMenu[80];
|
|
|
|
_idCmdFirst = idCmdFirst;
|
|
_uFlags = uFlags;
|
|
|
|
if (SUCCEEDED(PathFromDataObject(_pdtobj, _szPath, ARRAYSIZE(_szPath))))
|
|
{
|
|
// No openwith context menu for executables.
|
|
if (PathIsExe(_szPath))
|
|
return NOERROR;
|
|
|
|
pszExt = PathFindExtension(_szPath);
|
|
if (pszExt && *pszExt)
|
|
{
|
|
|
|
// Add Open/Edit/Default verb to extension app list
|
|
if (SUCCEEDED(AddVerbItems(pszExt)))
|
|
{
|
|
// Do this only if AddVerbItems succeeded; otherwise,
|
|
// we would create an empty MRU for a nonexisting class,
|
|
// causing the class to spring into existence and cause
|
|
// the "Open With" dialog to think we are overriding
|
|
// rather than creating new.
|
|
// get extension app list
|
|
_nItems = OpenWithListGetList(pszExt, NULL, &_pItems);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For known file type(there is at least one verb under its progid),
|
|
// if there is only one item in its openwithlist, don't show open with sub menu
|
|
if (1 == _nItems)
|
|
{
|
|
TCHAR szExe[MAX_PATH];
|
|
DWORD cch = ARRAYSIZE(szExe);
|
|
|
|
if (SUCCEEDED(AssocQueryString(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, pszExt, NULL, szExe, &cch)))
|
|
{
|
|
TCHAR szItem[MAX_PATH];
|
|
cch = ARRAYSIZE(szItem);
|
|
|
|
if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, _pItems[0].hKey, _pItems[0].pszVerb, szItem, &cch))
|
|
&& 0 == StrCmpI(szExe, szItem))
|
|
{
|
|
|
|
OpenWithListReleaseList(_pItems);
|
|
|
|
_pItems = NULL;
|
|
_nItems = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
LoadString(g_hinst, (_nItems ? IDS_OPENWITH : IDS_OPENWITHNEW), szOpenWithMenu, ARRAYSIZE(szOpenWithMenu));
|
|
|
|
if (_nItems)
|
|
{
|
|
_hMenu = CreatePopupMenu();
|
|
|
|
mii.cbSize = sizeof(MENUITEMINFO);
|
|
mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
|
|
mii.wID = idCmdFirst+1;
|
|
mii.fType = MFT_STRING;
|
|
mii.dwTypeData = szOpenWithMenu;
|
|
mii.dwItemData = 0;
|
|
|
|
InsertMenuItem(_hMenu,0,TRUE,&mii);
|
|
|
|
mii.fMask = MIIM_ID|MIIM_SUBMENU|MIIM_TYPE;
|
|
mii.fType = MFT_STRING;
|
|
mii.wID = idCmdFirst;
|
|
mii.hSubMenu = _hMenu;
|
|
mii.dwTypeData = szOpenWithMenu;
|
|
|
|
InsertMenuItem(hmenu,indexMenu,TRUE,&mii);
|
|
|
|
return ResultFromShort(_nItems + 2);
|
|
}
|
|
else
|
|
{
|
|
_hMenu = hmenu;
|
|
|
|
mii.cbSize = sizeof(MENUITEMINFO);
|
|
mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
|
|
mii.fType = MFT_STRING;
|
|
mii.wID = idCmdFirst;
|
|
mii.dwTypeData = szOpenWithMenu;
|
|
mii.dwItemData = 0;
|
|
|
|
InsertMenuItem(hmenu,indexMenu,TRUE,&mii);
|
|
|
|
return ResultFromShort(1);
|
|
}
|
|
}
|
|
|
|
HRESULT COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
CMINVOKECOMMANDINFOEX ici;
|
|
LPVOID pvFree;
|
|
|
|
// maybe these two routines should be collapsed into one?
|
|
if ((IS_INTRESOURCE(pici->lpVerb) || 0 == lstrcmpiA(pici->lpVerb, "openas"))
|
|
&& SUCCEEDED(ICI2ICIX(pici, &ici, &pvFree)))
|
|
{
|
|
SHELLEXECUTEINFO ei = {0};
|
|
|
|
if (SUCCEEDED(ICIX2SEI(&ici, &ei)))
|
|
{
|
|
POPENWITHITEM pItem;
|
|
ei.lpFile = _szPath;
|
|
|
|
if (IS_INTRESOURCE(pici->lpVerb))
|
|
pItem = GetItemData(_hMenu, LOWORD(pici->lpVerb));
|
|
else
|
|
pItem = NULL;
|
|
|
|
if (pItem)
|
|
{
|
|
// if pitem is there, this means that we are using
|
|
// something that was in the openwith list MRU.
|
|
|
|
ei.lpVerb = pItem->pszVerb;
|
|
ei.hkeyClass = pItem->hKey;
|
|
|
|
// make sure to update the MRU
|
|
OpenWithListRegister(0, PathFindExtension(_szPath), pItem->pszVerb, pItem->hKey);
|
|
}
|
|
else
|
|
{
|
|
// use the "Unknown" key so we get the openwith prompt
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("Unknown"), 0L, MAXIMUM_ALLOWED, &ei.hkeyClass);
|
|
|
|
if (!(_uFlags & CMF_DEFAULTONLY))
|
|
{
|
|
// defview sets CFM_DEFAULTONLY when the user is double-clicking. We check it
|
|
// here since we want do NOT want to query the class store if the user explicitly
|
|
// right-clicked on the menu and choo se openwith.
|
|
|
|
// pop up open with dialog without querying class store
|
|
ei.fMask |= SEE_MASK_NOQUERYCLASSSTORE;
|
|
}
|
|
}
|
|
|
|
// if we got the key then we are good to go!
|
|
if (ei.hkeyClass)
|
|
{
|
|
ei.fMask |= SEE_MASK_CLASSKEY;
|
|
|
|
#ifdef WINNT
|
|
// Shrink the shell since the user is about to run an application.
|
|
ShrinkWorkingSet();
|
|
#endif
|
|
|
|
if (FALSE != ShellExecuteEx(&ei))
|
|
{
|
|
hres = NOERROR;
|
|
#ifdef WINNT
|
|
// Shrink the shell since the user just ran an application.
|
|
ShrinkWorkingSet();
|
|
#endif
|
|
if (UEMIsLoaded())
|
|
{
|
|
// note that we already got a UIBL_DOTASSOC (from
|
|
// OpenAs_RunDLL or whatever it is that 'Unknown'
|
|
// runs). so the Uassist analysis app will have to
|
|
// subtract it off
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_RUNASSOC, UIBL_DOTNOASSOC);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_FAIL;
|
|
}
|
|
|
|
// Close the Unknown key if we opened it
|
|
if (!pItem && ei.hkeyClass)
|
|
{
|
|
RegCloseKey(ei.hkeyClass);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (pvFree)
|
|
LocalFree(pvFree);
|
|
}
|
|
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT COpenWithMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return HandleMenuMsg2(uMsg,wParam,lParam,NULL);
|
|
}
|
|
|
|
HRESULT COpenWithMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITMENUPOPUP:
|
|
{
|
|
InitMenuPopup(_hMenu);
|
|
}
|
|
break;
|
|
/*
|
|
case WM_DRAWITEM:
|
|
{
|
|
DRAWITEMSTRUCT * pdi = (DRAWITEMSTRUCT *)lParam;
|
|
|
|
DrawItem(pdi);
|
|
}
|
|
break;
|
|
|
|
case WM_MEASUREITEM:
|
|
{
|
|
MEASUREITEMSTRUCT *pmi = (MEASUREITEMSTRUCT *)lParam;
|
|
|
|
MeasureItem(pmi);
|
|
|
|
}
|
|
break;
|
|
case WM_MENUCHAR:
|
|
{
|
|
int c = GetMenuItemCount(_hMenu);
|
|
for (int i = 0; i < c; i++)
|
|
{
|
|
LPOPENWITHMENUITEMINFO lpomi = GetItemData(_hMenu, i);
|
|
if(lpomi && _MenuCharMatch(lpomi->szMenuText,(TCHAR)LOWORD(wParam),FALSE))
|
|
{
|
|
_lpomiLast = lpomi;
|
|
if(lResult) *lResult = MAKELONG(i,MNC_EXECUTE);
|
|
return S_OK;
|
|
}
|
|
}
|
|
if(lResult) *lResult = MAKELONG(0,MNC_IGNORE);
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
*/
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT COpenWithMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
|
|
{
|
|
if (_pdtobj)
|
|
_pdtobj->Release();
|
|
|
|
|
|
_pidlFolder = pidlFolder;
|
|
_pdtobj = pdtobj;
|
|
|
|
|
|
if (_pdtobj)
|
|
_pdtobj->AddRef();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
void COpenWithMenu::DrawItem(DRAWITEMSTRUCT *lpdi)
|
|
{
|
|
/*
|
|
if ((lpdi->itemAction & ODA_SELECT) || (lpdi->itemAction & ODA_DRAWENTIRE))
|
|
{
|
|
DWORD dwRop;
|
|
int x, y;
|
|
SIZE sz;
|
|
LPOPENWITHMENUITEMINFO lpomi = (LPOPENWITHMENUITEMINFO)lpdi->itemData;
|
|
|
|
// Draw the image (if there is one).
|
|
|
|
GetTextExtentPoint(lpdi->hDC, lpomi->szMenuText, lstrlen(lpomi->szMenuText), &sz);
|
|
|
|
if (lpdi->itemState & ODS_SELECTED)
|
|
{
|
|
SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
|
|
SetTextColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
|
|
// REVIEW HACK - keep track of the last selected item.
|
|
_lpomiLast = lpomi;
|
|
dwRop = SRCSTENCIL;
|
|
FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
|
|
}
|
|
else
|
|
{
|
|
dwRop = SRCAND;
|
|
SetTextColor(lpdi->hDC, GetSysColor(COLOR_MENUTEXT));
|
|
FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_MENU));
|
|
}
|
|
|
|
RECT rc = lpdi->rcItem;
|
|
rc.left += +2*CXIMAGEGAP+g_cxSmIcon;
|
|
|
|
|
|
DrawText(lpdi->hDC,lpomi->szMenuText,lstrlen(lpomi->szMenuText),
|
|
&rc,DT_SINGLELINE|DT_VCENTER);
|
|
if (lpomi->iImage != -1)
|
|
{
|
|
x = lpdi->rcItem.left+CXIMAGEGAP;
|
|
y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
|
|
ImageList_Draw(g_himlSysSmall, lpomi->iImage, lpdi->hDC, x, y, ILD_TRANSPARENT);
|
|
}
|
|
else
|
|
{
|
|
x = lpdi->rcItem.left+CXIMAGEGAP;
|
|
y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
LRESULT COpenWithMenu::MeasureItem(MEASUREITEMSTRUCT *lpmi)
|
|
{
|
|
LRESULT lres = FALSE;
|
|
/*
|
|
LPOPENWITHMENUITEMINFO lpomi = (LPOPENWITHMENUITEMINFO)lpmi->itemData;
|
|
if (lpomi)
|
|
{
|
|
// Get the rough height of an item so we can work out when to break the
|
|
// menu. User should really do this for us but that would be useful.
|
|
HDC hdc = GetDC(NULL);
|
|
if (hdc)
|
|
{
|
|
// REVIEW cache out the menu font?
|
|
NONCLIENTMETRICS ncm;
|
|
ncm.cbSize = SIZEOF(NONCLIENTMETRICS);
|
|
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, FALSE))
|
|
{
|
|
HFONT hfont = CreateFontIndirect(&ncm.lfMenuFont);
|
|
if (hfont)
|
|
{
|
|
SIZE sz;
|
|
HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
|
|
GetTextExtentPoint(hdc, lpomi->szMenuText, lstrlen(lpomi->szMenuText), &sz);
|
|
lpmi->itemHeight = max (g_cySmIcon+CXIMAGEGAP/2, ncm.iMenuHeight);
|
|
lpmi->itemWidth = g_cxSmIcon + 2*CXIMAGEGAP + sz.cx;
|
|
//lpmi->itemWidth = 2*CXIMAGEGAP + sz.cx;
|
|
SelectObject(hdc, hfontOld);
|
|
DeleteObject(hfont);
|
|
lres = TRUE;
|
|
}
|
|
}
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_OPENWITHMENU, TEXT("fm_mi: Filemenu is invalid."));
|
|
}
|
|
*/
|
|
return lres;
|
|
}
|
|
|
|
BOOL COpenWithMenu::InitMenuPopup(HMENU hmenu)
|
|
{
|
|
TCHAR szMenuText[80];
|
|
MENUITEMINFO mii;
|
|
|
|
TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::InitMenuPopup");
|
|
|
|
if (!_nItems || !_pItems)
|
|
return FALSE;
|
|
|
|
if (GetItemData(hmenu, 0)) // already initialized.
|
|
return FALSE;
|
|
|
|
// remove the place holder.
|
|
DeleteMenu(hmenu,0,MF_BYPOSITION);
|
|
|
|
// add app's in mru list to context menu
|
|
for (int i = 0; i < _nItems; i++)
|
|
{
|
|
mii.cbSize = sizeof(MENUITEMINFO);
|
|
mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
|
|
mii.wID = _idCmdFirst + i;
|
|
mii.fType = MFT_STRING;
|
|
mii.dwTypeData = _pItems[i].pszFriendly;
|
|
mii.dwItemData = (DWORD_PTR)&_pItems[i];
|
|
|
|
InsertMenuItem(hmenu,GetMenuItemCount(hmenu),TRUE,&mii);
|
|
}
|
|
|
|
// add seperator
|
|
AppendMenu(hmenu,MF_SEPARATOR,0,NULL);
|
|
|
|
// add "&Browse..."
|
|
LoadString(g_hinst, IDS_OPENWITHBROWSE, szMenuText, ARRAYSIZE(szMenuText));
|
|
mii.cbSize = sizeof(MENUITEMINFO);
|
|
mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
|
|
mii.wID = _idCmdFirst + _nItems + 1;
|
|
mii.fType = MFT_STRING;
|
|
mii.dwTypeData = szMenuText;
|
|
mii.dwItemData = 0;
|
|
|
|
InsertMenuItem(hmenu,GetMenuItemCount(hmenu),TRUE,&mii);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
POPENWITHITEM COpenWithMenu::GetItemData(HMENU hmenu, UINT iItem)
|
|
{
|
|
MENUITEMINFO mii;
|
|
|
|
mii.cbSize = SIZEOF(MENUITEMINFO);
|
|
mii.fMask = MIIM_DATA | MIIM_STATE;
|
|
mii.cch = 0; // just in case...
|
|
|
|
if (GetMenuItemInfo(hmenu, iItem, TRUE, &mii))
|
|
return (POPENWITHITEM)mii.dwItemData;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT COpenWithMenu::SetSite(IUnknown* pUnk)
|
|
{
|
|
ATOMICRELEASE(_pShellView2);
|
|
TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::SetSite = 0x%x", pUnk);
|
|
|
|
if(pUnk)
|
|
return pUnk->QueryInterface(IID_IShellView2,(void**)&_pShellView2);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT COpenWithMenu::GetSite(REFIID riid,void** ppvObj)
|
|
{
|
|
if(_pShellView2)
|
|
return _pShellView2->QueryInterface(riid,ppvObj);
|
|
else
|
|
{
|
|
ASSERT(ppvObj != NULL);
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|