480 lines
13 KiB
C
480 lines
13 KiB
C
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "datautil.h"
|
|
|
|
//
|
|
// This function is a callback function from property sheet page extensions.
|
|
//
|
|
BOOL CALLBACK _AddPropSheetPage(HPROPSHEETPAGE hpage, LPARAM lParam)
|
|
{
|
|
PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;
|
|
|
|
if (ppsh->nPages < MAX_FILE_PROP_PAGES)
|
|
{
|
|
ppsh->phpage[ppsh->nPages++] = hpage;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This function enumerates all the property sheet page extensions for
|
|
// specified class and let them add pages.
|
|
//
|
|
//
|
|
int DCA_AppendClassSheetInfo(HDCA hdca, HKEY hkeyProgID, LPPROPSHEETHEADER ppsh, IDataObject *pdtobj)
|
|
{
|
|
int i, iStart = -1;
|
|
for (i = 0; i < DCA_GetItemCount(hdca); i++)
|
|
{
|
|
IShellExtInit *psei;
|
|
// These came from HKCR hence need to go through administrator approval
|
|
if (DCA_ExtCreateInstance(hdca, i, &IID_IShellExtInit, &psei) == NOERROR)
|
|
{
|
|
IShellPropSheetExt *pspse;
|
|
if (SUCCEEDED(psei->lpVtbl->Initialize(psei, NULL, pdtobj, hkeyProgID))
|
|
&& SUCCEEDED(psei->lpVtbl->QueryInterface(psei, &IID_IShellPropSheetExt, &pspse)))
|
|
{
|
|
int nPagesSave = ppsh->nPages;
|
|
HRESULT hres = pspse->lpVtbl->AddPages(pspse, _AddPropSheetPage, (LPARAM)ppsh);
|
|
if (SUCCEEDED(hres) && hres != S_OK)
|
|
{
|
|
// Some shell extensions get confused and return S_FALSE when
|
|
// they didn't add anything, unaware that S_FALSE means "Please
|
|
// take the page I added and make it the default". So ignore
|
|
// the return value if it is out of range.
|
|
DWORD nPagesAdded = ppsh->nPages - nPagesSave;
|
|
DWORD nPageWanted = hres - 1;
|
|
if (nPageWanted < nPagesAdded)
|
|
iStart = nPagesSave + nPageWanted;
|
|
}
|
|
pspse->lpVtbl->Release(pspse);
|
|
}
|
|
psei->lpVtbl->Release(psei);
|
|
}
|
|
}
|
|
return iStart;
|
|
}
|
|
|
|
HWND FindStubForPidlClass(LPCITEMIDLIST pidl, int iClass)
|
|
{
|
|
HWND hwnd;
|
|
|
|
if (!pidl)
|
|
return NULL;
|
|
|
|
for (hwnd = FindWindow(c_szStubWindowClass, NULL); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
|
|
{
|
|
TCHAR szClass[80];
|
|
|
|
// find stub windows only
|
|
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
|
|
if (lstrcmpi(szClass, c_szStubWindowClass) == 0)
|
|
{
|
|
HANDLE hClassPidl;
|
|
DWORD dwProcId;
|
|
DWORD_PTR dwResult;
|
|
|
|
GetWindowThreadProcessId(hwnd, &dwProcId);
|
|
|
|
// since a propsheet could be doing work & not pumping messages, use a timeout
|
|
// we can almost just do this: hClassPidl = GetWindowLongPtr(hwnd, 0)
|
|
if (!SendMessageTimeout(hwnd, STUBM_GETDATA, 0, 0, SMTO_BLOCK, 3000, &dwResult))
|
|
continue;
|
|
hClassPidl = (HANDLE)dwResult;
|
|
if (hClassPidl)
|
|
{
|
|
LPBYTE lpb = (LPBYTE)SHLockShared(hClassPidl, dwProcId);
|
|
if (lpb)
|
|
{
|
|
int iClassFound = *(int *)lpb;
|
|
|
|
if (iClassFound == iClass &&
|
|
ILIsEqual(pidl, (LPITEMIDLIST)(lpb + sizeof(int))) )
|
|
{
|
|
SHUnlockShared(lpb);
|
|
return hwnd;
|
|
}
|
|
SHUnlockShared(lpb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
HANDLE _StuffStubWindow(HWND hwnd, LPITEMIDLIST pidlT, int iClass)
|
|
{
|
|
DWORD dwProcId;
|
|
HANDLE hSharedClassPidl;
|
|
UINT uidlSize;
|
|
|
|
uidlSize = ILGetSize(pidlT);
|
|
GetWindowThreadProcessId(hwnd, &dwProcId);
|
|
|
|
hSharedClassPidl = SHAllocShared(NULL, sizeof(int)+uidlSize, dwProcId);
|
|
if (hSharedClassPidl)
|
|
{
|
|
LPBYTE lpb = SHLockShared(hSharedClassPidl, dwProcId);
|
|
if (lpb)
|
|
{
|
|
*(int *)lpb = iClass;
|
|
memcpy(lpb+sizeof(int),pidlT, uidlSize);
|
|
SHUnlockShared(lpb);
|
|
SendMessage(hwnd, STUBM_SETDATA, (WPARAM)hSharedClassPidl, 0);
|
|
return hSharedClassPidl;
|
|
}
|
|
SHFreeShared(hSharedClassPidl, dwProcId);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Make sure we are the only stub window for this pidl/class.
|
|
//
|
|
// If so, saves information in the UNIQUESTUBINFO structure which keeps
|
|
// track of the uniqueness key. After you are done, you must pass the
|
|
// UNIQUESTUBINFO structure to FreeUniqueStub() to clean up the uniqueness
|
|
// key and destroy the stub window. Returns TRUE.
|
|
//
|
|
// If a stub window already exists for this pidl/class, then sets focus
|
|
// to the existing window that matches our uniqueness key and returns FALSE.
|
|
//
|
|
// In low memory conditions, plays it safe and declares the pidl/class
|
|
// unique.
|
|
//
|
|
//
|
|
// Example:
|
|
//
|
|
// UNIQUESTUBINFO usi;
|
|
// if (EnsureUniqueStub(pidl, STUBCLASS_PROPSHEET, NULL, &usi)) {
|
|
// DoStuff(usi.hwndStub, pidl);
|
|
// FreeUniqueStub(&usi);
|
|
// }
|
|
//
|
|
|
|
STDAPI_(BOOL)
|
|
EnsureUniqueStub(LPITEMIDLIST pidl, int iClass, POINT *ppt, UNIQUESTUBINFO *pusi)
|
|
{
|
|
HWND hwndOther;
|
|
|
|
ZeroMemory(pusi, sizeof(UNIQUESTUBINFO));
|
|
|
|
hwndOther = FindStubForPidlClass(pidl, iClass);
|
|
if (hwndOther)
|
|
{
|
|
SwitchToThisWindow(GetLastActivePopup(hwndOther), TRUE);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{ // Tag ourselves as the unique stub for this pidl/class
|
|
pusi->hwndStub = _CreateStubWindow(ppt, NULL);
|
|
|
|
// If no pidl, then nothing to tag *with*
|
|
// If no stub window, then nothing to attach the tag *to*
|
|
// But they are both still considered success.
|
|
|
|
if (pusi->hwndStub && pidl)
|
|
{
|
|
SHFILEINFO sfi;
|
|
|
|
pusi->hClassPidl = _StuffStubWindow(pusi->hwndStub, pidl, iClass);
|
|
|
|
if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_PIDL)) {
|
|
pusi->hicoStub = sfi.hIcon;
|
|
|
|
// Cannot stuff the title because the window might belong to another process
|
|
SendMessage(pusi->hwndStub, STUBM_SETICONTITLE, (WPARAM)pusi->hicoStub, 0);
|
|
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
STDAPI_(void) FreeUniqueStub(UNIQUESTUBINFO *pusi)
|
|
{
|
|
if (pusi->hwndStub)
|
|
DestroyWindow(pusi->hwndStub);
|
|
if (pusi->hClassPidl)
|
|
SHFreeShared(pusi->hClassPidl, GetCurrentProcessId());
|
|
if (pusi->hicoStub)
|
|
DestroyIcon(pusi->hicoStub);
|
|
}
|
|
|
|
BOOL _IsAnyDuplicatedKey(HKEY ahkeys[], UINT ckeys, HKEY hkey)
|
|
{
|
|
UINT ikey;
|
|
for (ikey=0; ikey<ckeys; ikey++)
|
|
{
|
|
if (ahkeys[ikey]==hkey) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STDAPI_(BOOL) SHOpenPropSheet(
|
|
LPCTSTR pszCaption,
|
|
HKEY ahkeys[],
|
|
UINT ckeys,
|
|
const CLSID * pclsidDef, OPTIONAL
|
|
IDataObject *pdtobj,
|
|
IShellBrowser * psb,
|
|
LPCTSTR pStartPage) OPTIONAL
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
BOOL fUnique;
|
|
PROPSHEETHEADER psh;
|
|
HPROPSHEETPAGE ahpage[MAX_FILE_PROP_PAGES];
|
|
HWND hwndStub = NULL;
|
|
STGMEDIUM medium;
|
|
HDCA hdca = NULL;
|
|
HICON hicoStuff = NULL;
|
|
UNIQUESTUBINFO usi;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszCaption, -1));
|
|
ASSERT(NULL == pclsidDef || IS_VALID_READ_PTR(pclsidDef, CLSID));
|
|
ASSERT(IS_VALID_CODE_PTR(pdtobj, DATAOBJECT));
|
|
ASSERT(NULL == psb || IS_VALID_CODE_PTR(psb, IShellBrowser));
|
|
ASSERT(NULL == pStartPage || IS_VALID_STRING_PTR(pStartPage, -1));
|
|
|
|
// Create the stub window
|
|
{
|
|
POINT pt;
|
|
POINT * ppt = NULL;
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
if (SUCCEEDED(DataObj_GetOFFSETs(pdtobj, &pt)))
|
|
ppt = &pt;
|
|
|
|
if (DataObj_GetHIDA(pdtobj, &medium))
|
|
{
|
|
HIDA hida = medium.hGlobal;
|
|
if (hida && (HIDA_GetCount(hida) == 1))
|
|
{
|
|
pidl = HIDA_ILClone(hida, 0);
|
|
}
|
|
HIDA_ReleaseStgMedium(NULL, &medium);
|
|
}
|
|
|
|
|
|
fUnique = EnsureUniqueStub(pidl, STUBCLASS_PROPSHEET, ppt, &usi);
|
|
ILFree(pidl);
|
|
}
|
|
|
|
// If there's already a property sheet up for this guy, then our job is done
|
|
if (!fUnique) {
|
|
return TRUE;
|
|
}
|
|
|
|
psh.hwndParent = usi.hwndStub;
|
|
psh.dwSize = sizeof(psh);
|
|
psh.dwFlags = PSH_PROPTITLE;
|
|
psh.hInstance = HINST_THISDLL;
|
|
psh.pszCaption = pszCaption;
|
|
psh.nPages = 0; // incremented in callback
|
|
psh.nStartPage = 0; // set below if specified
|
|
psh.phpage = ahpage;
|
|
if (pStartPage)
|
|
{
|
|
psh.dwFlags |= PSH_USEPSTARTPAGE;
|
|
psh.pStartPage = pStartPage;
|
|
}
|
|
|
|
hdca = DCA_Create();
|
|
if (hdca)
|
|
{
|
|
UINT ikey;
|
|
int nStartPage;
|
|
//
|
|
// Always add this default extention at the top, if any.
|
|
//
|
|
if (pclsidDef)
|
|
{
|
|
DCA_AddItem(hdca, pclsidDef);
|
|
}
|
|
|
|
for (ikey = 0; ikey < ckeys; ikey++)
|
|
{
|
|
if (ahkeys[ikey] && !_IsAnyDuplicatedKey(ahkeys, ikey, ahkeys[ikey]))
|
|
{
|
|
DCA_AddItemsFromKey(hdca, ahkeys[ikey], STRREG_SHEX_PROPSHEET);
|
|
}
|
|
}
|
|
|
|
// Notes: ahkeys[ckeys-1] as hkeyProgID
|
|
ASSERT(ckeys);
|
|
nStartPage = DCA_AppendClassSheetInfo(hdca, ahkeys[ckeys-1], &psh, pdtobj);
|
|
|
|
// If a shell extension requested to be the default and the caller didn't
|
|
// specify a preferred initial page, then let the shell extension win.
|
|
if (!pStartPage && nStartPage >= 0)
|
|
psh.nStartPage = nStartPage;
|
|
DCA_Destroy(hdca);
|
|
}
|
|
|
|
// Open the property sheet, only if we have some pages.
|
|
if (psh.nPages > 0)
|
|
{
|
|
_try
|
|
{
|
|
if (PropertySheet(&psh) >= 0) // IDOK or IDCANCEL (< 0 is error)
|
|
fSuccess = TRUE;
|
|
}
|
|
_except(UnhandledExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("PRSHT: Fault in property sheet"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShellMessageBox(HINST_THISDLL, NULL,
|
|
MAKEINTRESOURCE(IDS_NOPAGE),
|
|
MAKEINTRESOURCE(IDS_DESKTOP),
|
|
MB_OK|MB_ICONHAND);
|
|
}
|
|
|
|
// clean up the stub window and data
|
|
FreeUniqueStub(&usi);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
STDAPI_(BOOL) SHOpenPropSheetA(
|
|
LPCSTR pszCaption,
|
|
HKEY ahkeys[],
|
|
UINT ckeys,
|
|
const CLSID * pclsidDef,
|
|
IDataObject *pdtobj,
|
|
IShellBrowser * psb,
|
|
LPCSTR pszStartPage) OPTIONAL
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
if (IS_VALID_STRING_PTRA(pszCaption, MAX_PATH))
|
|
{
|
|
WCHAR wszCaption[MAX_PATH];
|
|
WCHAR wszStartPage[MAX_PATH];
|
|
|
|
SHAnsiToUnicode(pszCaption, wszCaption, SIZECHARS(wszCaption));
|
|
|
|
if (pszStartPage)
|
|
{
|
|
ASSERT(IS_VALID_STRING_PTRA(pszStartPage, MAX_PATH));
|
|
|
|
SHAnsiToUnicode(pszStartPage, wszStartPage, SIZECHARS(wszStartPage));
|
|
pszStartPage = (LPCSTR)wszStartPage;
|
|
}
|
|
|
|
bRet = SHOpenPropSheet(wszCaption, ahkeys, ckeys, pclsidDef, pdtobj, psb, (LPCWSTR)pszStartPage);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#else
|
|
|
|
STDAPI_(BOOL) SHOpenPropSheetW(
|
|
LPCWSTR pszCaption,
|
|
HKEY ahkeys[],
|
|
UINT ckeys,
|
|
const CLSID * pclsidDef,
|
|
IDataObject *pdtobj,
|
|
IShellBrowser * psb,
|
|
LPCWSTR pszStartPage) OPTIONAL
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
if (IS_VALID_STRING_PTRW(pszCaption, MAX_PATH))
|
|
{
|
|
char szCaption[MAX_PATH];
|
|
char szStartPage[MAX_PATH];
|
|
|
|
SHUnicodeToAnsi(pszCaption, szCaption, SIZECHARS(szCaption));
|
|
|
|
if (pszStartPage)
|
|
{
|
|
ASSERT(IS_VALID_STRING_PTRW(pszStartPage, MAX_PATH));
|
|
|
|
SHUnicodeToAnsi(pszStartPage, szStartPage, SIZECHARS(szStartPage));
|
|
pszStartPage = (LPCWSTR)szStartPage;
|
|
}
|
|
|
|
bRet = SHOpenPropSheet(szCaption, ahkeys, ckeys, pclsidDef, pdtobj, psb, (LPCSTR)pszStartPage);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#endif // UNICODE
|
|
|
|
//
|
|
// Async version of SHFormatDrive - creates a separate thread to do the
|
|
// format and returns immediately.
|
|
//
|
|
|
|
typedef struct {
|
|
HWND hwnd;
|
|
UINT drive;
|
|
UINT fmtID;
|
|
UINT options;
|
|
} FORMATTHREADINFO;
|
|
|
|
STDAPI_(DWORD) _FormatThreadProc(LPVOID lpParam)
|
|
{
|
|
FORMATTHREADINFO* pfi = (FORMATTHREADINFO*)lpParam;
|
|
LPITEMIDLIST pidl;
|
|
TCHAR szDrive[4];
|
|
|
|
lstrcpy(szDrive, TEXT("A:\\"));
|
|
ASSERT(pfi->drive < 26);
|
|
szDrive[0] += (TCHAR)pfi->drive;
|
|
|
|
pidl = ILCreateFromPath(szDrive);
|
|
if (pidl)
|
|
{
|
|
UNIQUESTUBINFO usi;
|
|
LPPOINT ppt = NULL;
|
|
RECT rcWindow;
|
|
if (pfi->hwnd)
|
|
{
|
|
GetWindowRect(pfi->hwnd, &rcWindow);
|
|
ppt = (LPPOINT)&rcWindow;
|
|
}
|
|
|
|
if (EnsureUniqueStub(pidl, STUBCLASS_FORMAT, ppt, &usi))
|
|
{
|
|
SHFormatDrive(usi.hwndStub, pfi->drive, pfi->fmtID, pfi->options);
|
|
FreeUniqueStub(&usi);
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
LocalFree(pfi);
|
|
return 0;
|
|
}
|
|
|
|
STDAPI_(void) SHFormatDriveAsync(
|
|
HWND hwnd,
|
|
UINT drive,
|
|
UINT fmtID,
|
|
UINT options
|
|
)
|
|
{
|
|
FORMATTHREADINFO* pfi = (FORMATTHREADINFO*)LocalAlloc(LPTR, sizeof(FORMATTHREADINFO));
|
|
if (pfi)
|
|
{
|
|
pfi->hwnd = hwnd;
|
|
pfi->drive = drive;
|
|
pfi->fmtID = fmtID;
|
|
pfi->options = options;
|
|
SHCreateThread(_FormatThreadProc, pfi, CTF_INSIST | CTF_PROCESS_REF, NULL);
|
|
}
|
|
}
|