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

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);
}
}