windows-nt/Source/XPSP1/NT/shell/shell32/fsassoc.cpp
2020-09-26 16:20:57 +08:00

1466 lines
40 KiB
C++

#include "shellprv.h"
#pragma hdrstop
#include <limits.h>
#include <shlwapi.h>
#include <objwindow.h>
#include "vdate.h"
#include "ids.h"
#include "fassoc.h"
STDAPI InitFileFolderClassNames(void);
STDAPI OpenWithListRegister(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszVerb, HKEY hkProgid);
#define AIF_TEMPKEY 0x1 // temp class key created for the selected exe
#define AIF_SHELLNEW 0x2 // class key with shellnew subkey
#define MAXKEYNAME 128
HRESULT _GetURL(BOOL fXMLLookup, LPCTSTR pszExt, LPTSTR pszURL, DWORD cchSize)
{
TCHAR szUrlTemplate[MAX_URL_STRING];
DWORD cbSize = sizeof(szUrlTemplate);
DWORD dwType;
LANGID nLangID = GetUserDefaultUILanguage();
HRESULT hr = S_OK;
LPCTSTR pszValue = (fXMLLookup ? TEXT("XMLLookup") : TEXT("Application"));
if (0x0409 != nLangID)
{
// We redirect to a single web page on intl so we can handle any languages we don't support
pszValue = TEXT("intl");
}
if ((ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Associations"),
pszValue, &dwType, (void *)szUrlTemplate, &cbSize)) &&
(REG_SZ == dwType))
{
wnsprintf(pszURL, cchSize, szUrlTemplate, nLangID, CharNext(pszExt));
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
HRESULT _OpenDownloadURL(HWND hwnd, LPCTSTR pszExt)
{
TCHAR szUrl[MAX_URL_STRING];
HRESULT hr = _GetURL(FALSE, pszExt, szUrl, ARRAYSIZE(szUrl));
if (SUCCEEDED(hr))
{
HINSTANCE hReturn = ShellExecute(hwnd, NULL, szUrl, NULL, NULL, SW_SHOWNORMAL);
if (hReturn < (HINSTANCE)32)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return S_OK;
}
class CInternetOpenAs : public CObjectWindow
{
public:
// *** IUnknown ***
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
HRESULT DisplayDialog(HWND hwndParent, LPCTSTR pszFile);
CInternetOpenAs(void);
private:
virtual ~CInternetOpenAs(void);
// Private Member Variables
long m_cRef;
LPTSTR _pszFilename;
LPTSTR _pszExt;
HWND _hwndParent;
// Private Member Functions
HRESULT _OnInitDlg(HWND hDlg);
HRESULT _OnCommand(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
HRESULT _OnNotify(HWND hDlg, LPARAM lParam);
// Download thread functions.
DWORD _DownloadThreadProc(void);
void _StartDownloadThread(void);
HRESULT _SetUnknownInfo(void);
HRESULT _ParseXML(BSTR bstrXML, LPTSTR pszFileType, DWORD cchSizeFileType, LPTSTR pszDescription, DWORD cchSizeDescription, LPTSTR pszUrl, DWORD cchSizeUrl, BOOL * pfUnknown);
INT_PTR _InternetOpenDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK InternetOpenDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam);
static DWORD CALLBACK DownloadThreadProc(void *pvThis) { return ((CInternetOpenAs *) pvThis)->_DownloadThreadProc(); };
};
#define WMUSER_CREATETOOLTIP (WM_USER + 1) // lParam is the hwndParent, wParam is the WSTR.
#define WMUSER_DESTROYTYPE (WM_USER + 2) // lParam wParam are 0
typedef CAppInfo APPINFO;
class COpenAs
{
public:
ULONG AddRef();
ULONG Release();
friend BOOL_PTR CALLBACK OpenAsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
friend BOOL_PTR CALLBACK NoOpenDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
friend HRESULT OpenAsDialog(HWND hwnd, POPENASINFO poainfo);
void OnOk();
private:
// params
HWND _hwnd; // parent window
POPENASINFO _poainfo;
// local data
long _cRef;
int _idDlg; // open as dialog type: DLG_OPENAS_NOTYPE or DLG_OPENAS
HWND _hDlg; // open as dialog window handle
HWND _hwndList; // app list
LPTSTR _pszExt;
TCHAR _szNoOpenMsg[MAX_PATH];
TCHAR _szDescription[CCH_KEYMAX]; // file type description
HRESULT _hr;
HTREEITEM _hItemRecommended; // root items to group programs
HTREEITEM _hItemOthers;
// constructer
COpenAs(HWND hwnd, POPENASINFO poainfo) : _hwnd(hwnd), _poainfo(poainfo), _cRef(1)
{
_pszExt = PathFindExtension(poainfo->pcszFile);
}
// other methods
HTREEITEM _AddAppInfoItem(APPINFO *pai, HTREEITEM hParentItem);
HTREEITEM _AddFromNewStorage(IAssocHandler *pah);
HTREEITEM _AddRootItem(BOOL bRecommended);
APPINFO *_TVFindAppInfo(HTREEITEM hItem);
HTREEITEM _TVFindItemByHandler(HTREEITEM hParentItem, LPCTSTR pszHandler);
UINT _FillListByEnumHandlers();
UINT _FillListWithHandlers();
void _InitOpenAsDlg();
BOOL RunAs(APPINFO *pai);
void OpenAsOther();
BOOL OpenAsMakeAssociation(LPCWSTR pszDesc, LPCWSTR pszHandler, HKEY hkey);
void _InitNoOpenDlg();
HRESULT _OpenAsDialog();
void _OnNotify(HWND hDlg, LPARAM lParam);
HRESULT _InternetOpen(void);
};
ULONG COpenAs::AddRef()
{
return ::InterlockedIncrement(&_cRef);
}
ULONG COpenAs::Release()
{
if (::InterlockedDecrement(&_cRef) == 0)
{
delete this;
return 0;
}
return _cRef;
}
STDAPI SHCreateAssocHandler(LPCWSTR pszExt, LPCWSTR pszApp, IAssocHandler **ppah);
void COpenAs::OpenAsOther()
{
TCHAR szApp[MAX_PATH];
TCHAR szPath[MAX_PATH];
*szApp = '\0';
SHExpandEnvironmentStrings(TEXT("%ProgramFiles%"), szPath, ARRAYSIZE(szPath));
// do a file open browse
if (GetFileNameFromBrowse(_hDlg, szApp, ARRAYSIZE(szApp), szPath,
MAKEINTRESOURCE(IDS_EXE), MAKEINTRESOURCE(IDS_PROGRAMSFILTER), MAKEINTRESOURCE(IDS_OPENAS)))
{
IAssocHandler *pah;
if (SUCCEEDED(SHCreateAssocHandler(_pszExt, szApp, &pah)))
{
CAppInfo *pai = new CAppInfo(pah);
if (pai)
{
HTREEITEM hItem = NULL;
if (pai->Init())
{
hItem = _TVFindItemByHandler(_hItemRecommended, pai->Name());
if (!hItem && _hItemOthers)
hItem = _TVFindItemByHandler(_hItemOthers, pai->Name());
if (!hItem)
{
hItem = _AddAppInfoItem(pai, _hItemOthers);
if (hItem)
pai = NULL;
}
}
// Select it
if (hItem)
{
TreeView_SelectItem(_hwndList, hItem);
SetFocus(_hwndList);
}
if (pai)
delete pai;
}
pah->Release();
}
}
}
HTREEITEM COpenAs::_AddAppInfoItem(APPINFO *pai, HTREEITEM hParentItem)
{
TVINSERTSTRUCT tvins = {0};
tvins.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
tvins.item.iSelectedImage = tvins.item.iImage = pai->IconIndex();
tvins.item.pszText = (LPWSTR) pai->UIName();
tvins.item.cchTextMax = lstrlen(pai->UIName())+1;
tvins.item.lParam = (LPARAM) pai;
tvins.hInsertAfter = TVI_SORT;
// If NULL, all programs are listed as root items
tvins.hParent = hParentItem;
return TreeView_InsertItem(_hwndList, &tvins);
}
HTREEITEM COpenAs::_AddFromNewStorage(IAssocHandler *pah)
{
HTREEITEM hitem = NULL;
CAppInfo *pai = new CAppInfo(pah);
if (pai)
{
// Trim duplicate items before we add them for other programs
if (pai->Init()
&& (!_hItemRecommended || !_TVFindItemByHandler(_hItemRecommended, pai->Name())))
{
hitem = _AddAppInfoItem(pai, S_OK == pah->IsRecommended() ? _hItemRecommended : _hItemOthers);
}
if (!hitem)
{
delete pai;
}
}
return hitem;
}
HTREEITEM COpenAs::_AddRootItem(BOOL bRecommended)
{
TCHAR sz[MAX_PATH];
int iLen = LoadString(g_hinst, (bRecommended? IDS_OPENWITH_RECOMMENDED : IDS_OPENWITH_OTHERS), sz, ARRAYSIZE(sz));
if (iLen)
{
TVINSERTSTRUCT tvins = {0};
tvins.item.mask = TVIF_TEXT | TVIF_STATE |TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvins.item.pszText = sz;
tvins.item.cchTextMax = iLen;
tvins.item.stateMask = tvins.item.state = TVIS_EXPANDED; // Expand child items by default
tvins.hInsertAfter = TVI_ROOT;
tvins.hParent = NULL;
//
// Currently, we use program icon.
// Change it if PM/UI designer have more appropriate one.
//
tvins.item.iSelectedImage = tvins.item.iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_STPROGS, 0);
return TreeView_InsertItem(_hwndList, &tvins);
}
return NULL;
}
APPINFO *COpenAs::_TVFindAppInfo(HTREEITEM hItem)
{
// if hItem not specified, use current selected item
if (!hItem)
hItem = TreeView_GetSelection(_hwndList);
if (hItem)
{
TVITEM tvi = {0};
tvi.mask = TVIF_HANDLE;
tvi.hItem = hItem;
if (TreeView_GetItem(_hwndList, &tvi))
return ((APPINFO *) tvi.lParam);
}
return NULL;
}
HTREEITEM COpenAs::_TVFindItemByHandler(HTREEITEM hParentItem, LPCTSTR pszHandler)
{
// if we have parent item, search its children, otherwise search root items
HTREEITEM hItem = TreeView_GetNextItem(_hwndList, hParentItem, hParentItem ? TVGN_CHILD : TVGN_ROOT );
while (hItem)
{
APPINFO *pai = _TVFindAppInfo(hItem);
if (pai && !StrCmpI(pai->Name(), pszHandler))
return hItem;
hItem = TreeView_GetNextItem(_hwndList, hItem, TVGN_NEXT);
}
return NULL;
}
UINT COpenAs::_FillListByEnumHandlers()
{
IEnumAssocHandlers *penum;
UINT cHandlers = 0;
if (SUCCEEDED(SHAssocEnumHandlers(_pszExt, &penum)))
{
HTREEITEM hitemFocus = NULL;
BOOL fFirst = TRUE;
IAssocHandler *pah;
while (S_OK == penum->Next(1, &pah, NULL))
{
if (fFirst)
{
//
// Group programs to "recommended" and "others" only when we can get two different group of programs
// Otherwise, all programs are listed as root items
// Note: in our storage, general handlers is always a superset of extension related handlers
//
// if the first item is recommended,
// then we add the recommended node
//
if (S_OK == pah->IsRecommended())
{
_hItemRecommended = _AddRootItem(TRUE);
_hItemOthers = _AddRootItem(FALSE);
}
fFirst = FALSE;
}
HTREEITEM hitem = _AddFromNewStorage(pah);
if (!hitemFocus && hitem && S_OK == pah->IsRecommended())
{
// we put focus on the first recommended item
// the enum starts with the best
hitemFocus = hitem;
}
cHandlers++;
}
if (cHandlers && _hItemRecommended)
{
if (!hitemFocus)
hitemFocus = TreeView_GetNextItem(_hwndList, _hItemRecommended, TVGN_CHILD);
TreeView_SelectItem(_hwndList, hitemFocus);
}
penum->Release();
}
return cHandlers;
}
UINT COpenAs::_FillListWithHandlers()
{
UINT cHandlers = _FillListByEnumHandlers();
//
// Set focus on the first recommended program if we have program groups
// Otherwise, all programs are root items, focus will be set to the first item by default
//
return cHandlers;
}
void COpenAs::_InitOpenAsDlg()
{
TCHAR szFileName[MAX_PATH];
BOOL fDisableAssociate;
HIMAGELIST himl;
RECT rc;
// Don't let the file name go beyond the width of one line...
lstrcpy(szFileName, PathFindFileName(_poainfo->pcszFile));
GetClientRect(GetDlgItem(_hDlg, IDD_TEXT), &rc);
PathCompactPath(NULL, szFileName, rc.right - 4 * GetSystemMetrics(SM_CXBORDER));
SetDlgItemText(_hDlg, IDD_FILE_TEXT, szFileName);
// AraBern 07/20/99, specific to TS on NT, but can be used on NT without TS
// this restriction doesnt apply to admins
if (SHRestricted(REST_NOFILEASSOCIATE) && !IsUserAnAdmin())
{
CheckDlgButton(_hDlg, IDD_MAKEASSOC, FALSE);
ShowWindow(GetDlgItem(_hDlg, IDD_MAKEASSOC), SW_HIDE);
}
else
{
// Don't allow associations to be made for things we consider exes...
fDisableAssociate = (! (_poainfo->dwInFlags & OAIF_ALLOW_REGISTRATION) ||
PathIsExe(_poainfo->pcszFile));
// check IDD_MAKEASSOC only for unknown file type and those with OAIF_FORCE_REGISTRATION flag set
if ((_poainfo->dwInFlags & OAIF_FORCE_REGISTRATION) ||
(_idDlg != DLG_OPENAS && !fDisableAssociate))
{
CheckDlgButton(_hDlg, IDD_MAKEASSOC, TRUE);
}
if (fDisableAssociate)
EnableWindow(GetDlgItem(_hDlg, IDD_MAKEASSOC), FALSE);
}
_hwndList = GetDlgItem(_hDlg, IDD_APPLIST);
Shell_GetImageLists(NULL, &himl);
TreeView_SetImageList(_hwndList, himl, TVSIL_NORMAL);
// Leave space between ICON images - SM_CXEDGE
TreeView_SetItemHeight(_hwndList, TreeView_GetItemHeight(_hwndList) + GetSystemMetrics(SM_CXEDGE));
if (!_FillListWithHandlers())
{
// lets force the expensive walk
IRunnableTask *ptask;
if (SUCCEEDED(CTaskEnumHKCR_Create(&ptask)))
{
ptask->Run();
ptask->Release();
_FillListWithHandlers();
}
}
// initialize the OK button
EnableWindow(GetDlgItem(_hDlg, IDOK), (TreeView_GetSelection(_hwndList) != NULL));
InitFileFolderClassNames();
}
BOOL COpenAs::RunAs(APPINFO *pai)
{
pai->Handler()->Exec(_hwnd, _poainfo->pcszFile);
SHAddToRecentDocs(SHARD_PATH, _poainfo->pcszFile);
return TRUE;
}
void COpenAs::_InitNoOpenDlg()
{
SHFILEINFO sfi;
HICON hIcon;
TCHAR szFormat[MAX_PATH], szTemp[MAX_PATH];
GetDlgItemText(_hDlg, IDD_TEXT1, szFormat, ARRAYSIZE(szFormat));
wnsprintf(szTemp, ARRAYSIZE(szTemp), szFormat, _szDescription, _pszExt);
SetDlgItemText(_hDlg, IDD_TEXT1, szTemp);
if (*_szNoOpenMsg)
SetDlgItemText(_hDlg, IDD_TEXT2, _szNoOpenMsg);
if (SHGetFileInfo(_poainfo->pcszFile, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON)
&& NULL != sfi.hIcon)
{
hIcon = sfi.hIcon;
}
else
{
HIMAGELIST himl;
Shell_GetImageLists(&himl, NULL);
hIcon = ImageList_ExtractIcon(g_hinst, himl, II_DOCNOASSOC);
}
hIcon = (HICON)SendDlgItemMessage(_hDlg, IDD_ICON, STM_SETICON, (WPARAM)hIcon, 0);
if ( hIcon )
{
DestroyIcon(hIcon);
}
}
HRESULT COpenAs::_InternetOpen(void)
{
HRESULT hr = E_OUTOFMEMORY;
CInternetOpenAs * pInternetOpenAs = new CInternetOpenAs();
if (pInternetOpenAs)
{
DWORD dwValue;
DWORD cbSize = sizeof(dwValue);
DWORD dwType;
hr = S_OK;
if ((ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"), TEXT("NoInternetOpenWith"), &dwType, (void *)&dwValue, &cbSize)) || (0 == dwValue))
{
// If the policy is not set, use the feature.
hr = pInternetOpenAs->DisplayDialog(_hwnd, _poainfo->pcszFile);
}
pInternetOpenAs->Release();
}
return hr;
}
class COpenAsAssoc
{
public:
COpenAsAssoc(PCWSTR pszExt);
~COpenAsAssoc() {ATOMICRELEASE(_pqa);}
BOOL HasClassKey();
BOOL HasCommand();
BOOL GetDescription(PWSTR psz, DWORD cch);
BOOL GetNoOpen(PWSTR psz, DWORD cch);
protected:
IQueryAssociations *_pqa;
HRESULT _hrInit;
};
COpenAsAssoc::COpenAsAssoc(PCWSTR pszExt)
{
AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &_pqa));
if (FAILED(_pqa->Init(0, pszExt, NULL, NULL)))
ATOMICRELEASE(_pqa);
}
BOOL COpenAsAssoc::HasClassKey()
{
BOOL fRet = FALSE;
if (_pqa)
{
HKEY hk;
if (SUCCEEDED(_pqa->GetKey(0, ASSOCKEY_CLASS, NULL, &hk)))
{
RegCloseKey(hk);
fRet = TRUE;
}
}
return fRet;
}
BOOL COpenAsAssoc::HasCommand()
{
DWORD cch;
if (_pqa)
return SUCCEEDED(_pqa->GetString(0, ASSOCSTR_COMMAND, NULL, NULL, &cch));
return FALSE;
}
BOOL COpenAsAssoc::GetDescription(PWSTR psz, DWORD cch)
{
if (_pqa)
return SUCCEEDED(_pqa->GetString(0, ASSOCSTR_FRIENDLYDOCNAME, NULL, psz, &cch));
return FALSE;
}
BOOL COpenAsAssoc::GetNoOpen(PWSTR psz, DWORD cch)
{
if (_pqa)
return SUCCEEDED(_pqa->GetString(0, ASSOCSTR_NOOPEN, NULL, psz, &cch));
return FALSE;
}
const PCWSTR s_rgImageExts[] =
{
{ TEXT(".bmp")},
{ TEXT(".dib")},
{ TEXT(".emf")},
{ TEXT(".gif")},
{ TEXT(".jfif")},
{ TEXT(".jpg")},
{ TEXT(".jpe")},
{ TEXT(".jpeg")},
{ TEXT(".png")},
{ TEXT(".tif")},
{ TEXT(".tiff")},
{ TEXT(".wmf")},
{ NULL}
};
BOOL _IsImageExt(PCWSTR pszExt)
{
for (int i = 0; s_rgImageExts[i] ; i++)
{
if (0 == StrCmpIW(pszExt, s_rgImageExts[i]))
return TRUE;
}
return FALSE;
}
static const PCWSTR s_rgZipExts[] =
{
{ TEXT(".zip")},
{ NULL}
};
static const struct
{
const PCWSTR *rgpszExts;
PCWSTR pszDll;
} s_rgFixAssocs[] = {
{ s_rgImageExts, L"shimgvw.dll" },
{ s_rgZipExts, L"zipfldr.dll" },
// { s_rgWmpExts, L"wmp.dll" },
};
PCWSTR _WhichDll(PCWSTR pszExt)
{
for (int i = 0; i < ARRAYSIZE(s_rgFixAssocs); i++)
{
for (int j = 0; s_rgFixAssocs[i].rgpszExts[j] ; j++)
{
if (0 == StrCmpIW(pszExt, s_rgFixAssocs[i].rgpszExts[j]))
return s_rgFixAssocs[i].pszDll;
}
}
return NULL;
}
BOOL _CreateProcessWithArgs(LPCTSTR pszApp, LPCTSTR pszArgs, LPCTSTR pszDirectory, PROCESS_INFORMATION *ppi)
{
STARTUPINFO si = {0};
si.cb = sizeof(si);
TCHAR szCommandLine[MAX_PATH * 2];
wnsprintf(szCommandLine, ARRAYSIZE(szCommandLine), L"\"%s\" %s", pszApp, pszArgs);
return CreateProcess(pszApp, szCommandLine, NULL, NULL, FALSE, 0, NULL, pszDirectory, &si, ppi);
}
void _GetSystemPathItem(PCWSTR pszItem, PWSTR pszPath, DWORD cch)
{
GetSystemDirectory(pszPath, cch);
PathCombine(pszPath, pszPath, pszItem);
}
BOOL _Regsvr32Dll(PCWSTR pszDll)
{
WCHAR szReg[MAX_PATH];
WCHAR szDll[MAX_PATH + 3] = L"/s ";
_GetSystemPathItem(L"regsvr32.exe", szReg, ARRAYSIZE(szReg));
_GetSystemPathItem(pszDll, szDll + 3, ARRAYSIZE(szDll) - 3);
PROCESS_INFORMATION pi = {0};
if (_CreateProcessWithArgs(szReg, szDll, NULL, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return TRUE;
}
return FALSE;
}
BOOL _FixAssocs(PCWSTR pszExt)
{
PCWSTR pszDll = _WhichDll(pszExt);
if (pszDll)
{
_Regsvr32Dll(pszDll);
COpenAsAssoc oac(pszExt);
return oac.HasCommand();
}
return FALSE;
}
HRESULT COpenAs::_OpenAsDialog()
{
BOOL fHasCommand = FALSE;
int idDlg = DLG_OPENAS_NOTYPE;
// Depending on policy, do not allow user to change file type association.
if (SHRestricted(REST_NOFILEASSOCIATE))
{
_poainfo->dwInFlags &= ~OAIF_ALLOW_REGISTRATION & ~OAIF_REGISTER_EXT;
}
// We don't allow association for files without extension or with only "." as extension
if (!_pszExt || !*_pszExt || !*(_pszExt+1))
{
idDlg = DLG_OPENAS;
_poainfo->dwInFlags &= ~OAIF_ALLOW_REGISTRATION;
}
// Known file type(has verb): use DLG_OPENAS
// NoOpen file type(has NoOpen value): use DLG_NOOPEN
// Unknown file type(All others): use DLG_OPENAS_NOTYPE
else
{
COpenAsAssoc oac(_pszExt);
fHasCommand = oac.HasCommand();
if (oac.HasClassKey())
{
idDlg = DLG_OPENAS;
oac.GetDescription(_szDescription, ARRAYSIZE(_szDescription));
if (oac.GetNoOpen(_szNoOpenMsg, ARRAYSIZE(_szNoOpenMsg))
&& !fHasCommand)
{
INITCOMMONCONTROLSEX initComctl32;
initComctl32.dwSize = sizeof(initComctl32);
initComctl32.dwICC = (ICC_STANDARD_CLASSES | ICC_LINK_CLASS);
InitCommonControlsEx(&initComctl32); // Register the comctl32 LinkWindow
if ((-1 != DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_NOOPEN), _hwnd, NoOpenDlgProc, (LPARAM)this))
&& _hr == S_FALSE)
{
// user selected cancel
return _hr;
}
}
}
// if this is a busted file association, maybe we can fix it...
if ((OAIF_REGISTER_EXT & _poainfo->dwInFlags) && !fHasCommand)
{
// this feels like an unknown type
if (_FixAssocs(_pszExt))
{
SHChangeNotify(SHCNE_ASSOCCHANGED, 0, NULL, NULL);
// Exec if requested.
if (_poainfo->dwInFlags & OAIF_EXEC)
{
IAssocHandler *pah;
if (SUCCEEDED(SHCreateAssocHandler(_pszExt, NULL, &pah)))
{
CAppInfo *pai = new CAppInfo(pah);
if (pai)
{
if (pai->Init())
{
RunAs(pai);
}
delete pai;
}
pah->Release();
}
}
return S_OK;
}
}
}
_idDlg = idDlg;
HRESULT hr = _hr;
LinkWindow_RegisterClass();
// If this is the dialog where we don't know the file type and the feature is turned on,
// use the Internet Open As dialog.
if ((FALSE == fHasCommand) &&
SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("InternetOpenWith"), FALSE, TRUE))
{
hr = _InternetOpen();
}
// Display the old dialog if fUseInternetOpenAs is NOT set. Or display it if the user
// chooses "Choose..." in that dialog.
if (SUCCEEDED(hr))
{
INITCOMMONCONTROLSEX initComctl32;
initComctl32.dwSize = sizeof(initComctl32);
initComctl32.dwICC = (ICC_STANDARD_CLASSES | ICC_LINK_CLASS);
InitCommonControlsEx(&initComctl32); // Register the comctl32 LinkWindow
if (-1 == DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(idDlg), _hwnd, OpenAsDlgProc, (LPARAM)this))
{
hr = E_FAIL;
}
}
return hr;
}
BOOL_PTR CALLBACK NoOpenDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
COpenAs *pOpenAs = (COpenAs *)GetWindowLongPtr(hDlg, DWLP_USER);
switch (wMsg)
{
case WM_INITDIALOG:
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
pOpenAs = (COpenAs *)lParam;
pOpenAs->_hDlg = hDlg;
pOpenAs->_InitNoOpenDlg();
break;
case WM_COMMAND:
ASSERT(pOpenAs);
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDD_OPENWITH:
// this will cause the open with dialog
// to follow this dialog
pOpenAs->_hr = S_OK;
EndDialog(hDlg, TRUE);
break;
case IDCANCEL:
pOpenAs->_hr = S_FALSE;
EndDialog(hDlg, TRUE);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
const static DWORD aOpenAsHelpIDs[] = { // Context Help IDs
IDD_ICON, IDH_FCAB_OPENAS_APPLIST,
IDD_TEXT, IDH_FCAB_OPENAS_APPLIST,
IDD_FILE_TEXT, (DWORD) -1,
IDD_DESCRIPTIONTEXT, IDH_FCAB_OPENAS_DESCRIPTION,
IDD_DESCRIPTION, IDH_FCAB_OPENAS_DESCRIPTION,
IDD_APPLIST, IDH_FCAB_OPENAS_APPLIST,
IDD_MAKEASSOC, IDH_FCAB_OPENAS_MAKEASSOC,
IDD_OTHER, IDH_FCAB_OPENAS_OTHER,
IDD_OPENWITH_BROWSE, IDH_FCAB_OPENAS_OTHER,
IDD_OPENWITH_WEBSITE, IDH_FCAB_OPENWITH_LOOKONWEB,
0, 0
};
const static DWORD aOpenAsDownloadHelpIDs[] = { // Context Help IDs
IDD_ICON, (DWORD) -1,
IDD_FILE_TEXT, (DWORD) -1,
// For DLG_OPENAS_DOWNALOAD
IDD_WEBAUTOLOOKUP, IDH_CANNOTOPEN_USEWEB,
IDD_OPENWITHLIST, IDH_CANNOTOPEN_SELECTLIST,
0, 0
};
void COpenAs::_OnNotify(HWND hDlg, LPARAM lParam)
{
switch (((NMHDR *)lParam)->code)
{
case TVN_DELETEITEM:
if (lParam)
{
APPINFO *pai = (APPINFO *)(((LPNMTREEVIEW) lParam )->itemOld.lParam);
if (pai)
{
delete pai;
}
}
break;
case TVN_SELCHANGED:
EnableWindow(GetDlgItem(hDlg, IDOK), (_TVFindAppInfo(TreeView_GetSelection(NULL)) != NULL));
break;
case NM_DBLCLK:
if (IsWindowEnabled(GetDlgItem(hDlg, IDOK)))
PostMessage(hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(IDOK, hDlg, 0));
break;
case NM_RETURN:
case NM_CLICK:
if (lParam)
{
PNMLINK pNMLink = (PNMLINK) lParam;
if (!StrCmpW(pNMLink->item.szID, L"Browse"))
{
_OpenDownloadURL(_hwnd, _pszExt);
EndDialog(hDlg, FALSE);
}
}
break;
}
}
void COpenAs::OnOk()
{
APPINFO *pai = _TVFindAppInfo(NULL);
if (pai)
{
// See if we should make an association or not...
GetDlgItemText(_hDlg, IDD_DESCRIPTION, _szDescription, ARRAYSIZE(_szDescription));
if ((_poainfo->dwInFlags & OAIF_REGISTER_EXT)
&& (IsDlgButtonChecked(_hDlg, IDD_MAKEASSOC)))
{
pai->Handler()->MakeDefault(_szDescription);
}
// Did we register the association?
_hr = IsDlgButtonChecked(_hDlg, IDD_MAKEASSOC) ? S_OK : S_FALSE;
// Exec if requested.
if (_poainfo->dwInFlags & OAIF_EXEC)
{
RunAs(pai);
}
EndDialog(_hDlg, TRUE);
}
}
BOOL_PTR CALLBACK OpenAsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
COpenAs *pOpenAs = (COpenAs *)GetWindowLongPtr(hDlg, DWLP_USER);
switch (wMsg)
{
case WM_INITDIALOG:
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
pOpenAs = (COpenAs *)lParam;
if (pOpenAs)
{
pOpenAs->_hDlg = hDlg;
pOpenAs->_InitOpenAsDlg();
}
break;
case WM_HELP:
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aOpenAsHelpIDs);
break;
case WM_CONTEXTMENU:
if ((int)SendMessage(hDlg, WM_NCHITTEST, 0, lParam) != HTCLIENT)
return FALSE; // don't process it
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
(ULONG_PTR)(void *)aOpenAsHelpIDs);
break;
case WM_NOTIFY:
if (pOpenAs)
{
pOpenAs->_OnNotify(hDlg, lParam);
}
break;
case WM_COMMAND:
ASSERT(pOpenAs);
if (pOpenAs)
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDD_OPENWITH_BROWSE:
pOpenAs->OpenAsOther();
break;
case IDOK:
{
pOpenAs->OnOk();
}
break;
case IDCANCEL:
pOpenAs->_hr = E_ABORT;
EndDialog(hDlg, FALSE);
break;
}
}
break;
default:
return FALSE;
}
return TRUE;
}
// external API version
HRESULT
OpenAsDialog(
HWND hwnd,
POPENASINFO poainfo)
{
HRESULT hr = E_OUTOFMEMORY;
COpenAs *pOpenAs = new COpenAs(hwnd, poainfo);
DebugMsg(DM_TRACE, TEXT("Enter OpenAs for %s"), poainfo->pcszFile);
if (pOpenAs)
{
hr = pOpenAs->_OpenAsDialog();
pOpenAs->Release();
}
return hr;
}
void WINAPI OpenAs_RunDLL(HWND hwnd, HINSTANCE hAppInstance, LPSTR lpszCmdLine, int nCmdShow)
{
HRESULT hrOle = SHCoInitialize(); // Needed for SysLink's IAccessability (LresultFromObject)
OPENASINFO oainfo = { 0 };
UINT iLen = lstrlenA(lpszCmdLine)+1;
LPWSTR lpwszCmdLine;
lpwszCmdLine = (LPWSTR)LocalAlloc(LPTR,iLen*sizeof(WCHAR));
if (lpwszCmdLine)
{
MultiByteToWideChar(CP_ACP, 0,
lpszCmdLine, -1,
lpwszCmdLine, iLen);
DebugMsg(DM_TRACE, TEXT("OpenAs_RunDLL is called with (%s)"), lpwszCmdLine);
oainfo.pcszFile = lpwszCmdLine;
oainfo.dwInFlags = (OAIF_ALLOW_REGISTRATION |
OAIF_REGISTER_EXT |
OAIF_EXEC);
OpenAsDialog(hwnd, &oainfo);
LocalFree(lpwszCmdLine);
}
if (SUCCEEDED(hrOle))
{
CoUninitialize();
}
}
void WINAPI OpenAs_RunDLLW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
{
HRESULT hrOle = SHCoInitialize(); // Needed for SysLink's IAccessability (LresultFromObject)
OPENASINFO oainfo = { 0 };
DebugMsg(DM_TRACE, TEXT("OpenAs_RunDLL is called with (%s)"), lpwszCmdLine);
oainfo.pcszFile = lpwszCmdLine;
oainfo.dwInFlags = (OAIF_ALLOW_REGISTRATION |
OAIF_REGISTER_EXT |
OAIF_EXEC);
OpenAsDialog(hwnd, &oainfo);
if (SUCCEEDED(hrOle))
{
CoUninitialize();
}
}
#ifdef DEBUG
//
// Type checking
//
const static RUNDLLPROCA lpfnRunDLL = OpenAs_RunDLL;
const static RUNDLLPROCW lpfnRunDLLW = OpenAs_RunDLLW;
#endif
//===========================
// *** Private Methods ***
//===========================
HRESULT CreateWindowTooltip(HWND hDlg, HWND hwndWindow, LPCTSTR pszText)
{
HRESULT hr = E_OUTOFMEMORY;
HWND hwndToolTipo = CreateWindow(TOOLTIPS_CLASS, c_szNULL, WS_POPUP | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, hDlg, NULL, HINST_THISDLL, NULL);
if (hwndToolTipo)
{
TOOLINFO ti;
ti.cbSize = sizeof(ti);
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
ti.hwnd = hDlg;
ti.uId = (UINT_PTR)hwndWindow;
ti.lpszText = (LPTSTR)pszText; // const -> non const
ti.hinst = HINST_THISDLL;
SendMessage(hwndToolTipo, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
hr = S_OK;
}
return hr;
}
HRESULT CInternetOpenAs::_ParseXML(BSTR bstrXML, LPTSTR pszFileType, DWORD cchSizeFileType, LPTSTR pszDescription, DWORD cchSizeDescription, LPTSTR pszUrl, DWORD cchSizeUrl, BOOL * pfUnknown)
{
IXMLDOMDocument * pXMLDoc;
HRESULT hr = XMLDOMFromBStr(bstrXML, &pXMLDoc);
*pfUnknown = FALSE;
pszFileType[0] = pszDescription[0] = pszUrl[0] = 0;
if (SUCCEEDED(hr))
{
IXMLDOMElement * pXMLElement = NULL;
hr = pXMLDoc->get_documentElement(&pXMLElement);
if (S_FALSE == hr)
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
else if (SUCCEEDED(hr))
{
// This is only valid XML if the root tag is "MSFILEASSOCIATIONS".
// The case is not important.
hr = XMLElem_VerifyTagName(pXMLElement, L"MSFILEASSOCIATIONS");
if (SUCCEEDED(hr))
{
CComBSTR bstrFileType;
hr = XMLNode_GetChildTagTextValue(pXMLElement, L"FILETYPENAME", &bstrFileType);
if (SUCCEEDED(hr))
{
CComBSTR bstrDesc;
CComBSTR bstrURL;
StrCpyN(pszFileType, bstrFileType, cchSizeFileType);
if (SUCCEEDED(XMLNode_GetChildTagTextValue(pXMLElement, L"DESCRIPTION", &bstrDesc)))
{
StrCpyN(pszDescription, bstrDesc, cchSizeDescription);
}
else
{
StrCpyN(pszDescription, L"", cchSizeDescription);
}
hr = XMLNode_GetChildTagTextValue(pXMLElement, L"URL", &bstrURL);
if (SUCCEEDED(hr))
{
CComBSTR bstrUnknown;
StrCpyN(pszUrl, bstrURL, cchSizeUrl);
if (SUCCEEDED(XMLNode_GetChildTagTextValue(pXMLElement, L"UNKNOWN", &bstrUnknown)) &&
!StrCmpIW(bstrUnknown, L"TRUE"))
{
*pfUnknown = TRUE;
}
}
}
}
pXMLElement->Release();
}
pXMLDoc->Release();
}
return hr;
}
DWORD CInternetOpenAs::_DownloadThreadProc(void)
{
#ifdef FEATURE_DOWNLOAD_DESCRIPTION
// 1. Create the URL
TCHAR szUrl[MAX_PATH];
if (SUCCEEDED(_GetURL(TRUE, _pszExt, szUrl, ARRAYSIZE(szUrl))))
{
// 2. Download the XML
BSTR bstrXML;
HRESULT hr = DownloadUrl(szUrl, &bstrXML);
if (SUCCEEDED(hr))
{
TCHAR szFileType[MAX_PATH];
TCHAR szDescription[2000];
BOOL fUnknown = FALSE;
// 3. Get the info from the XML to the UI
hr = _ParseXML(bstrXML, szFileType, ARRAYSIZE(szFileType), szDescription, ARRAYSIZE(szDescription), szUrl, ARRAYSIZE(szUrl), &fUnknown);
if (SUCCEEDED(hr) && IsWindow(_hwnd))
{
SetWindowText(GetDlgItem(_hwnd, IDD_FILETYPE_TEXT), szFileType);
SendMessage(_hwnd, WMUSER_CREATETOOLTIP, (WPARAM)szDescription, (LPARAM)GetDlgItem(_hwnd, IDD_FILETYPE_TEXT));
if (fUnknown)
{
// Hide the "Type" control.
SendMessage(_hwnd, WMUSER_DESTROYTYPE, NULL, NULL);
}
}
SysFreeString(bstrXML);
}
if (FAILED(hr))
{
_SetUnknownInfo();
}
}
#endif // FEATURE_DOWNLOAD_DESCRIPTION
Release();
return 0;
}
HRESULT CInternetOpenAs::_SetUnknownInfo(void)
{
// Hide the "Type" control.
#ifdef FEATURE_DOWNLOAD_DESCRIPTION
EnableWindow(GetDlgItem(_hwnd, IDD_FILETYPE_LABLE), FALSE);
EnableWindow(GetDlgItem(_hwnd, IDD_FILETYPE_TEXT), FALSE);
ShowWindow(GetDlgItem(_hwnd, IDD_FILETYPE_LABLE), SW_HIDE);
ShowWindow(GetDlgItem(_hwnd, IDD_FILETYPE_TEXT), SW_HIDE);
#endif // FEATURE_DOWNLOAD_DESCRIPTION
return S_OK;
}
void CInternetOpenAs::_StartDownloadThread(void)
{
#ifdef FEATURE_DOWNLOAD_DESCRIPTION
AddRef();
if (!SHCreateThread(CInternetOpenAs::DownloadThreadProc, this, (CTF_COINIT | CTF_PROCESS_REF | CTF_FREELIBANDEXIT), NULL))
{
// We failed so don't leave the background thread with a ref.
Release();
_SetUnknownInfo();
}
#endif // FEATURE_DOWNLOAD_DESCRIPTION
}
HRESULT CInternetOpenAs::_OnInitDlg(HWND hDlg)
{
_hwnd = hDlg;
// Start the background thread to download the information.
_StartDownloadThread();
SetWindowText(GetDlgItem(_hwnd, IDD_FILE_TEXT), _pszFilename);
CheckDlgButton(hDlg, IDD_WEBAUTOLOOKUP, BST_CHECKED);
return S_OK;
}
HRESULT CInternetOpenAs::_OnCommand(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UINT idCtrl = GET_WM_COMMAND_ID(wParam, lParam);
UINT wEvent = GET_WM_COMMAND_CMD(wParam, lParam);
switch (idCtrl)
{
case IDCANCEL:
EndDialog(hDlg, E_FAIL);
break;
case IDOK:
if (BST_UNCHECKED != IsDlgButtonChecked(hDlg, IDD_WEBAUTOLOOKUP))
{
_OpenDownloadURL(_hwnd, _pszExt);
EndDialog(hDlg, E_FAIL);
}
else
{
EndDialog(hDlg, S_OK); // return S_OK so it will open the next dialog.
}
break;
}
return S_OK;
}
HRESULT CInternetOpenAs::_OnNotify(HWND hDlg, LPARAM lParam)
{
switch (((NMHDR *)lParam)->code)
{
case NM_CLICK:
if (lParam)
{
PNMLINK pNMLink = (PNMLINK) lParam;
if (!StrCmpW(pNMLink->item.szID, L"GoOnline"))
{
_OpenDownloadURL(_hwnd, _pszExt);
EndDialog(hDlg, E_FAIL);
}
else if (!StrCmpW(pNMLink->item.szID, L"Choose"))
{
EndDialog(hDlg, S_OK); // return S_OK so it will open the next dialog.
}
}
break;
}
return S_OK;
}
INT_PTR CALLBACK CInternetOpenAs::InternetOpenDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
CInternetOpenAs * pThis = (CInternetOpenAs *)GetWindowLongPtr(hDlg, DWLP_USER);
if (WM_INITDIALOG == wMsg)
{
pThis = (CInternetOpenAs *) lParam;
if (pThis)
{
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
}
}
if (pThis)
return pThis->_InternetOpenDlgProc(hDlg, wMsg, wParam, lParam);
return DefWindowProc(hDlg, wMsg, wParam, lParam);
}
// This Property Sheet appear in the top level of the "Display Control Panel".
INT_PTR CInternetOpenAs::_InternetOpenDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG:
_OnInitDlg(hDlg);
break;
case WM_COMMAND:
_OnCommand(hDlg, message, wParam, lParam);
break;
case WM_NOTIFY:
_OnNotify(hDlg, lParam);
break;
case WM_HELP:
WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (DWORD_PTR) aOpenAsDownloadHelpIDs);
break;
case WM_CONTEXTMENU: // right mouse click
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (DWORD_PTR) aOpenAsDownloadHelpIDs);
break;
case WMUSER_CREATETOOLTIP:
CreateWindowTooltip(_hwnd, (HWND)lParam, (LPCWSTR)wParam);
break;
case WMUSER_DESTROYTYPE:
#ifdef FEATURE_DOWNLOAD_DESCRIPTION
EnableWindow(GetDlgItem(_hwnd, IDD_FILETYPE_LABLE), FALSE);
EnableWindow(GetDlgItem(_hwnd, IDD_FILETYPE_TEXT), FALSE);
ShowWindow(GetDlgItem(_hwnd, IDD_FILETYPE_LABLE), SW_HIDE);
ShowWindow(GetDlgItem(_hwnd, IDD_FILETYPE_TEXT), SW_HIDE);
#endif // FEATURE_DOWNLOAD_DESCRIPTION
break;
}
return FALSE;
}
//===========================
// *** Public Methods ***
//===========================
HRESULT CInternetOpenAs::DisplayDialog(HWND hwnd, LPCTSTR pszFile)
{
HRESULT hr = E_OUTOFMEMORY;
INITCOMMONCONTROLSEX initComctl32;
initComctl32.dwSize = sizeof(initComctl32);
initComctl32.dwICC = (ICC_STANDARD_CLASSES | ICC_LINK_CLASS);
InitCommonControlsEx(&initComctl32); // Register the comctl32 LinkWindow
Str_SetPtrW(&_pszFilename, (PathFindFileName(pszFile) ? PathFindFileName(pszFile) : pszFile));
Str_SetPtrW(&_pszExt, PathFindExtension(_pszFilename));
if (_pszExt)
{
_hwndParent = hwnd;
hr = (HRESULT) DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_OPENAS_DOWNALOAD), _hwnd, CInternetOpenAs::InternetOpenDlgProc, (LPARAM)this);
}
return hr;
}
//===========================
// *** IUnknown Interface ***
//===========================
ULONG CInternetOpenAs::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG CInternetOpenAs::Release()
{
if (InterlockedDecrement(&m_cRef))
return m_cRef;
delete this;
return 0;
}
HRESULT CInternetOpenAs::QueryInterface(REFIID riid, void **ppvObj)
{
HRESULT hr = E_NOINTERFACE;
static const QITAB qit[] =
{
QITABENT(CInternetOpenAs, IOleWindow),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
//===========================
// *** Class Methods ***
//===========================
CInternetOpenAs::CInternetOpenAs(void) : m_cRef(1)
{
DllAddRef();
// This needs to be allocated in Zero Inited Memory.
// Assert that all Member Variables are inited to Zero.
ASSERT(!_pszExt);
}
CInternetOpenAs::~CInternetOpenAs()
{
Str_SetPtrW(&_pszExt, NULL);
Str_SetPtrW(&_pszFilename, NULL);
DllRelease();
}