mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4423 lines
133 KiB
4423 lines
133 KiB
//--------------------------------------------------------------------------
|
|
// Init the Cabinet (ie the top level browser).
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Includes...
|
|
#include "cabinet.h"
|
|
#include "rcids.h"
|
|
#include "drivlist.h"
|
|
#include "cabwnd.h"
|
|
#include "tree.h"
|
|
#include "onetree.h"
|
|
#include <regstr.h>
|
|
#include "cabdde.h"
|
|
|
|
#ifdef DEBUG
|
|
extern UINT wDebugMask;
|
|
#define DEBUG_BREAK Assert(FALSE)
|
|
#else
|
|
#define DEBUG_BREAK (0)
|
|
#endif
|
|
|
|
int WinMainT(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpszCmdLine, int nCmdShow);
|
|
|
|
|
|
// from win32\kernel\utctime.c (private)
|
|
DWORD APIENTRY RefreshDaylightInformation(BOOL fChangeTime);
|
|
|
|
// shelldll\binder.c
|
|
void WINAPI SHFreeUnusedLibraries();
|
|
void WINAPI SHAbortInvokeCommand();
|
|
|
|
// Note This value is extracted from setupx.h...
|
|
#define SUF_FIRSTTIME 0x00000001L
|
|
|
|
#define SVSI_SELFLAGS (SVSI_SELECT|SVSI_FOCUSED|SVSI_DESELECTOTHERS|SVSI_ENSUREVISIBLE)
|
|
|
|
CABINETSTATE g_CabState = { 0 };
|
|
BOOL g_fNoDesktop = FALSE;
|
|
COLORREF g_crAltColor = RGB(0,0,255);
|
|
CRITICAL_SECTION g_csThreads = { 0 };
|
|
HICON g_hIconDefOpenLarge = NULL;
|
|
HICON g_hIconDefOpenSmall = NULL;
|
|
HKEY g_hkeyExplorer = NULL;
|
|
|
|
BOOL g_fRunSeparateDesktop = FALSE; // Seperate explorer process for the desktop
|
|
BOOL g_fRunSeparateStartAndStay = TRUE; // Start always and stay OR only on demand and timed exit
|
|
BOOL g_fRunNoUI = FALSE; // Tell explorer to go into stay (or demand) mode
|
|
BOOL g_fShowCompColor = FALSE; // Display compressed items in a different color
|
|
|
|
// Define structure to be used at head of state stream that is
|
|
// not dependent on 16 or 32 bits...
|
|
typedef struct _CABSH // Cabinet Stream header
|
|
{
|
|
DWORD dwSize; // Offset to where the View streamed additional info
|
|
|
|
// First stuff from the window placement
|
|
DWORD flags;
|
|
DWORD showCmd;
|
|
POINTL ptMinPosition;
|
|
POINTL ptMaxPosition;
|
|
RECTL rcNormalPosition;
|
|
|
|
// Stuff from Folder Settings;
|
|
DWORD ViewMode; // View mode (FOLDERVIEWMODE values)
|
|
DWORD fFlags; // View options (FOLDERFLAGS bits)
|
|
DWORD TreeSplit; // Position of split in pixels (BUGBUG?)
|
|
|
|
// Hot Key
|
|
DWORD dwHotkey; // Hotkey
|
|
|
|
WINVIEW wv;
|
|
} CABSH;
|
|
|
|
UINT g_msgMSWheel;
|
|
#define MSH_MOUSEWHEEL "MSWHEEL_ROLLMSG"
|
|
|
|
BOOL Cabinet_CreateAppGlobals(const CLSID *pclsid,
|
|
LPCITEMIDLIST pidlRoot);
|
|
HWND Cabinet_FindByPidl(LPCITEMIDLIST pidl);
|
|
|
|
BOOL Cabinet_IsExplorerWindow(HWND hwnd)
|
|
{
|
|
TCHAR szClass[CCHSZSHORT];
|
|
|
|
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
|
|
return lstrcmpi(szClass, c_szExploreClass) == 0;
|
|
}
|
|
|
|
|
|
BOOL Cabinet_IsFolderWindow(HWND hwnd)
|
|
{
|
|
TCHAR szClass[CCHSZSHORT];
|
|
|
|
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
|
|
return lstrcmpi(szClass, c_szCabinetClass) == 0;
|
|
}
|
|
|
|
|
|
//
|
|
LRESULT CALLBACK DrivesWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND hwndToolbar = GetParent(hWnd);
|
|
PFileCabinet pfc;
|
|
//
|
|
pfc = GetPFC(GetParent(hwndToolbar));
|
|
switch (uMessage)
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
{
|
|
MSG msg;
|
|
HWND hwndTips;
|
|
msg.lParam = lParam;
|
|
msg.wParam = wParam;
|
|
msg.message = uMessage;
|
|
msg.hwnd = hWnd;
|
|
hwndTips = (HWND)SendMessage(hwndToolbar, TB_GETTOOLTIPS, 0, 0L);
|
|
SendMessage(hwndTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&msg);
|
|
break;
|
|
}
|
|
case WM_SETFOCUS:
|
|
// This is gross, but if the window was destroyed that had the
|
|
// focus this would fail and we would not get this to the
|
|
// combo box. This happens if you click on the combobox while
|
|
// in name editing mode.
|
|
if (wParam && !IsWindow((HWND)wParam))
|
|
wParam = 0;
|
|
}
|
|
|
|
return(CallWindowProc(pfc->lpfnDrives, hWnd, uMessage, wParam, lParam));
|
|
}
|
|
TCHAR const c_szToolbarClass[] = TOOLBARCLASSNAME;
|
|
TCHAR const c_szComboBox[] = TEXT("combobox");
|
|
TCHAR const c_szInstallExe[] = TEXT("install.exe");
|
|
|
|
//---------------------------------------------------------------------------
|
|
BOOL _CreateToolbar(PFileCabinet pfc)
|
|
{
|
|
HWND hwndTips;
|
|
TOOLINFO ti;
|
|
#if 0
|
|
pfc->hwndToolbar = CreateToolbarEx(pfc->hwndMain, TBSTYLE_TOOLTIPS | WS_CHILD | WS_CLIPSIBLINGS,
|
|
FCIDM_TOOLBAR, 21, hinstCabinet, IDB_FSTOOLBAR, NULL,
|
|
0, 0, 0, 0, 0, SIZEOF(TBBUTTON));
|
|
#else
|
|
|
|
pfc->hwndToolbar = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOOLWINDOW,
|
|
c_szToolbarClass, NULL,
|
|
WS_CHILD | TBSTYLE_TOOLTIPS | WS_CLIPSIBLINGS, // | CCS_ADJUSTABLE | TBSTYLE_WRAPABLE,
|
|
0, 0, 100, 30, pfc->hwndMain, (HMENU)FCIDM_TOOLBAR, hinstCabinet, NULL);
|
|
if (pfc->hwndToolbar)
|
|
{
|
|
TBADDBITMAP ab;
|
|
|
|
// this tells the toolbar what version we are
|
|
SendMessage(pfc->hwndToolbar, TB_BUTTONSTRUCTSIZE, SIZEOF(TBBUTTON), 0);
|
|
|
|
ab.hInst = HINST_COMMCTRL; // take them from commctrl
|
|
ab.nID = IDB_STD_SMALL_COLOR; // standard toolbar images
|
|
pfc->iStdTBOffset = (int)SendMessage(pfc->hwndToolbar, TB_ADDBITMAP, 0, (LPARAM)&ab);
|
|
|
|
ab.nID = IDB_VIEW_SMALL_COLOR; // std view bitmaps
|
|
pfc->iTBOffset = (int)SendMessage(pfc->hwndToolbar, TB_ADDBITMAP, 11, (LPARAM)&ab);
|
|
}
|
|
#endif
|
|
|
|
if (!pfc->hwndToolbar)
|
|
{
|
|
DEBUG_BREAK;
|
|
return FALSE;
|
|
}
|
|
|
|
// make sure pfc->hwndToolbar is set so the combo box
|
|
// measure item messages work
|
|
|
|
pfc->hwndDrives = CreateWindow(c_szComboBox, NULL, WS_BORDER |
|
|
WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED,
|
|
-32000, -32000, 10, 10,
|
|
pfc->hwndToolbar, (HMENU)FCIDM_DRIVELIST, hinstCabinet, NULL);
|
|
if (!pfc->hwndDrives)
|
|
{
|
|
DEBUG_BREAK;
|
|
DestroyWindow(pfc->hwndToolbar);
|
|
pfc->hwndToolbar = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
SendMessage(pfc->hwndDrives, CB_SETEXTENDEDUI, TRUE, 0L);
|
|
pfc->lpfnDrives = (WNDPROC)GetWindowLong(pfc->hwndDrives, GWL_WNDPROC);
|
|
SetWindowLong(pfc->hwndDrives, GWL_WNDPROC, (LONG)DrivesWndProc);
|
|
|
|
hwndTips = (HWND)SendMessage(pfc->hwndToolbar, TB_GETTOOLTIPS, 0, 0L);
|
|
|
|
if (hwndTips) {
|
|
ti.cbSize = SIZEOF(ti);
|
|
ti.uFlags = TTF_IDISHWND | TTF_CENTERTIP;
|
|
ti.hwnd = pfc->hwndMain;
|
|
ti.uId = (UINT)pfc->hwndDrives;
|
|
ti.lpszText = MAKEINTRESOURCE(IDS_TT_DRIVES);
|
|
ti.hinst = hinstCabinet;
|
|
SendMessage(hwndTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HWND CreateTitles(PFileCabinet pfc)
|
|
{
|
|
HFONT hfont;
|
|
TCHAR szTitle[80];
|
|
LoadString(hinstCabinet, IDS_TREETITLE, szTitle, ARRAYSIZE(szTitle));
|
|
pfc->hwndTreeTitle = CreateWindowEx(0, c_szStatic, szTitle,
|
|
WS_CHILD|WS_VISIBLE| SS_LEFTNOWORDWRAP | SS_NOPREFIX | SS_SUNKEN | SS_CENTERIMAGE,
|
|
0,0,0,0, pfc->hwndMain, NULL, hinstCabinet, NULL);
|
|
|
|
pfc->hwndViewTitle = CreateWindowEx(0, c_szStatic, NULL,
|
|
WS_CHILD|WS_VISIBLE| SS_LEFTNOWORDWRAP | SS_NOPREFIX | SS_SUNKEN | SS_CENTERIMAGE,
|
|
0,0,0,0, pfc->hwndMain, NULL, hinstCabinet, NULL);
|
|
|
|
if (pfc->hwndTreeTitle) {
|
|
HDC hdc;
|
|
TEXTMETRIC tm;
|
|
HFONT hfontOld;
|
|
NONCLIENTMETRICS ncm;
|
|
|
|
ncm.cbSize = SIZEOF(ncm);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, 0);
|
|
|
|
hfont = CreateFontIndirect(&ncm.lfStatusFont);
|
|
hdc = GetDC(pfc->hwndTreeTitle);
|
|
if (hfont) {
|
|
SendMessage(pfc->hwndTreeTitle, WM_SETFONT, (WPARAM)hfont, FALSE);
|
|
SendMessage(pfc->hwndViewTitle, WM_SETFONT, (WPARAM)hfont, FALSE);
|
|
|
|
hfontOld = SelectObject(hdc, hfont);
|
|
}
|
|
|
|
GetTextMetrics(hdc, &tm);
|
|
pfc->iTitleHeight = tm.tmHeight + tm.tmInternalLeading + 2*g_cyEdge;
|
|
|
|
if (hfont) {
|
|
SelectObject(hdc, hfontOld);
|
|
ReleaseDC(pfc->hwndTreeTitle, hdc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUG_BREAK;
|
|
}
|
|
return pfc->hwndTreeTitle;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
HWND CreateTreeview(HWND hwndCabinet)
|
|
{
|
|
HWND hwndTree;
|
|
|
|
hwndTree = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, NULL,
|
|
WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_EDITLABELS | TVS_HASLINES,
|
|
0, 0, 0, 0, hwndCabinet, (HMENU)FCIDM_TREE, hinstCabinet, NULL);
|
|
|
|
|
|
if (hwndTree) {
|
|
TreeView_SetImageList(hwndTree, g_himlSysSmall, TVSIL_NORMAL);
|
|
#ifdef TESTTVSTATE
|
|
TreeView_SetImageList(hwndTree, g_himlSysSmall, TVSIL_STATE);
|
|
#endif
|
|
} else {
|
|
DEBUG_BREAK;
|
|
}
|
|
|
|
return hwndTree;
|
|
}
|
|
|
|
#ifdef WANT_TABS
|
|
BOOL _CreateTabs(PFileCabinet pfc)
|
|
{
|
|
pfc->hwndTabs = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD, // | WS_VISIBLE, // | WS_CLIPSIBLINGS,
|
|
0, 0, 10, 10, pfc->hwndMain, (HMENU)FCIDM_TABS, hinstCabinet, NULL);
|
|
|
|
if (!pfc->hwndTabs)
|
|
{
|
|
DEBUG_BREAK;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
#define TMPL_VIEW 0
|
|
#define TMPL_OTHER 1
|
|
#define TMPL_EXPL_VIEW 2
|
|
#define TMPL_EXPL_OTHER 3
|
|
|
|
HMENU g_hmTemplate[4] =
|
|
{
|
|
NULL, NULL, NULL, NULL,
|
|
} ;
|
|
|
|
BOOL Cabinet_IsTemplateMenu(HMENU hm)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<ARRAYSIZE(g_hmTemplate); ++i)
|
|
{
|
|
if (hm == g_hmTemplate[i])
|
|
{
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
HMENU Cabinet_MenuTemplate(BOOL bViewer, BOOL bExplorer)
|
|
{
|
|
int nhmGlobal;
|
|
LPCTSTR pszTemplate;
|
|
|
|
if (bViewer)
|
|
{
|
|
pszTemplate = MAKEINTRESOURCE(MENU_TEMPLATE);
|
|
nhmGlobal = 0;
|
|
}
|
|
else
|
|
{
|
|
pszTemplate = MAKEINTRESOURCE(MENU_FULL);
|
|
nhmGlobal = 1;
|
|
}
|
|
if (bExplorer)
|
|
{
|
|
nhmGlobal |= 2;
|
|
}
|
|
|
|
if (!g_hmTemplate[nhmGlobal])
|
|
{
|
|
g_hmTemplate[nhmGlobal] = LoadMenu(hinstCabinet, pszTemplate);
|
|
|
|
//
|
|
// We need to remove "Tools" menu if this is not an explorer.
|
|
//
|
|
if (g_hmTemplate[nhmGlobal])
|
|
{
|
|
if (!bExplorer)
|
|
{
|
|
DeleteMenu(g_hmTemplate[nhmGlobal],
|
|
FCIDM_MENU_TOOLS, MF_BYCOMMAND);
|
|
}
|
|
|
|
else if ( (!(GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS)) ||
|
|
SHRestricted(REST_NONETCONNECTDISCONNECT) )
|
|
{
|
|
// We need to get rid of Connect/Disconnect menu items
|
|
int i;
|
|
for (i=FCIDM_CONNECT; i<= FCIDM_CONNECT_SEP; i++)
|
|
{
|
|
DeleteMenu(g_hmTemplate[nhmGlobal],
|
|
i, MF_BYCOMMAND);
|
|
}
|
|
}
|
|
|
|
if (SHRestricted(REST_NOFILEMENU)) {
|
|
DeleteMenu(g_hmTemplate[nhmGlobal],
|
|
FCIDM_MENU_FILE, MF_BYCOMMAND);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(g_hmTemplate[nhmGlobal]);
|
|
}
|
|
|
|
#if 0
|
|
const TCHAR c_szArial[] = TEXT(TEXT("Arial"));
|
|
|
|
HRGN GetTextRegion(HDC hdc, LPCTSTR pszText, LPCTSTR pszFace, int dy)
|
|
{
|
|
LOGFONT lf;
|
|
HFONT hfont;
|
|
HRGN hrgn = NULL;
|
|
|
|
SystemParametersInfo(SPI_GETICONTITLELOGFONT, SIZEOF(lf), &lf, FALSE);
|
|
|
|
lstrcpyn(lf.lfFaceName, pszFace ? pszFace : c_szArial, ARRAYSIZE(lf.lfFaceName));
|
|
lf.lfHeight = dy ? -dy : -100;
|
|
lf.lfWidth = 0;
|
|
lf.lfWeight = FW_BOLD;
|
|
lf.lfClipPrecision |= CLIP_TT_ALWAYS;
|
|
|
|
hfont = CreateFontIndirect(&lf);
|
|
if (hfont)
|
|
{
|
|
HFONT hfontOld = SelectObject(hdc, hfont);
|
|
if (hfontOld)
|
|
{
|
|
RECT rc;
|
|
int iBkMode = SetBkMode(hdc, TRANSPARENT);
|
|
|
|
BeginPath(hdc);
|
|
// TextOut(hdc, 0, 0, pszText, lstrlen(pszText));
|
|
rc.top = rc.left = 0;
|
|
rc.bottom = rc.right = 1000;
|
|
DrawText(hdc, pszText, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_NOCLIP);
|
|
EndPath(hdc);
|
|
|
|
hrgn = PathToRegion(hdc);
|
|
|
|
SetBkMode(hdc, iBkMode );
|
|
|
|
SelectObject(hdc, hfontOld);
|
|
}
|
|
DeleteObject(hfont);
|
|
}
|
|
|
|
return hrgn;
|
|
}
|
|
#endif
|
|
|
|
#define Window_SetHotkey(hwnd, wHotkey) SendMessage(hwnd, WM_SETHOTKEY, wHotkey, 0)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Handle creation of a new folder window. Creates everything except the
|
|
// viewer part.
|
|
// Returns -1 if something goes wrong.
|
|
LRESULT Cabinet_OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
|
|
{
|
|
PCABVIEW pcv = lpcs->lpCreateParams;
|
|
PFileCabinet pfc = CreateFileCabinet(hwnd, pcv->wv.bTree); // hwnd => pfc->hwndMain
|
|
if (pfc)
|
|
{
|
|
SetPFC(hwnd, pfc);
|
|
|
|
pfc->wv = pcv->wv; // Store the window visible states
|
|
pfc->TreeSplit = pcv->TreeSplit;
|
|
|
|
// Create all child windows
|
|
//
|
|
#if defined(WINDOWS_ME)
|
|
//
|
|
// note there is no sizegrip. this interfers with right-aligned text.
|
|
// all me code should be unifdef'ed in win96, and tested against
|
|
// SM_MIDEASTENABLED instead
|
|
//
|
|
pfc->hwndStatus = CreateStatusWindow(WS_CHILD | SBT_RTLREADING | WS_CLIPSIBLINGS,
|
|
NULL, hwnd, FCIDM_STATUS);
|
|
#else
|
|
pfc->hwndStatus = CreateStatusWindow(WS_CHILD | SBARS_SIZEGRIP | WS_CLIPSIBLINGS,
|
|
NULL, hwnd, FCIDM_STATUS);
|
|
#endif
|
|
Window_SetHotkey(hwnd, pcv->wHotkey);
|
|
|
|
if (pfc->hwndStatus) {
|
|
|
|
// if we don't want to tree,
|
|
// or if we want the tree and creating the tree succeeds
|
|
if (!(pcv->wv.bTree) ||
|
|
((NULL != (pfc->hwndTree = CreateTreeview(hwnd))) &&
|
|
CreateTitles(pfc))) // assignment, not compare
|
|
{
|
|
CTreeDropTarget_Register(pfc);
|
|
|
|
if (_CreateToolbar(pfc))
|
|
{
|
|
#ifdef WANT_TABS
|
|
if (!(pcv->bTabs) ||
|
|
_CreateTabs(pfc)) {
|
|
#endif
|
|
static HACCEL hAccelLoad = NULL;
|
|
|
|
HFONT hfont = (HFONT)SendMessage(pfc->hwndToolbar, WM_GETFONT, 0, 0);
|
|
|
|
SendMessage(pfc->hwndDrives, WM_SETFONT, (WPARAM)hfont, 0);
|
|
|
|
pfc->hmenuCur = Cabinet_MenuTemplate(TRUE, (BOOL)pfc->hwndTree);
|
|
SetMenu(hwnd, pfc->hmenuCur);
|
|
|
|
if (hAccelLoad == NULL)
|
|
{
|
|
hAccelLoad = LoadAccelerators(hinstCabinet, MAKEINTRESOURCE(ACCEL_MERGE));
|
|
}
|
|
pfc->hMainAccel = hAccelLoad;
|
|
|
|
// we need this even if we don't have the tree up
|
|
// to get notified in case we get deleted/renamed/moved
|
|
OTRegister(pfc->hwndMain);
|
|
FolderList_RegisterWindow(pfc->hwndMain,NULL);
|
|
|
|
#if 0
|
|
{
|
|
HDC hdc = GetDC(pfc->hwndMain);
|
|
HRGN hrgn = GetTextRegion(hdc, TEXT("Chris\r\nGuzak\r\nWindows 95"), NULL, 30);
|
|
if (hrgn)
|
|
SetWindowRgn(pfc->hwndMain, hrgn, FALSE);
|
|
ReleaseDC(pfc->hwndMain, hdc);
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
#ifdef WANT_TABS
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (pfc->hwndTree)
|
|
DestroyWindow(pfc->hwndTree);
|
|
pfc->hwndTree = NULL;
|
|
}
|
|
DestroyWindow(pfc->hwndStatus);
|
|
pfc->hwndStatus = NULL;
|
|
}
|
|
}
|
|
// Note that pfc will be released in WM_DESTROY
|
|
|
|
DebugMsg(DM_ERROR, TEXT("c.hfwc: Unable to create all folder windows."));
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
BOOL bDefStatusBar : 1;
|
|
BOOL bDefToolBarSingle : 1;
|
|
BOOL bDefToolBarMulti : 1;
|
|
|
|
UINT uDefViewMode;
|
|
} DEFFOLDERSETTINGS;
|
|
|
|
DEFFOLDERSETTINGS g_dfs = {
|
|
TRUE,
|
|
TRUE,
|
|
FALSE,
|
|
FVM_ICON
|
|
};
|
|
|
|
void SaveDefaultFolderSettings()
|
|
{
|
|
HKEY hkCabStreams;
|
|
|
|
if (RegOpenKey(g_hkeyExplorer, c_szCabinetStreams, &hkCabStreams) == ERROR_SUCCESS)
|
|
{
|
|
|
|
RegSetValueEx(hkCabStreams, (LPCTSTR)c_szSettings,
|
|
0L, REG_BINARY, (LPBYTE)&g_dfs,
|
|
SIZEOF(g_dfs));
|
|
|
|
RegCloseKey(hkCabStreams);
|
|
}
|
|
}
|
|
|
|
void InitDefaultFolderSettings()
|
|
{
|
|
HKEY hkCabStreams;
|
|
|
|
if (g_fCleanBoot) {
|
|
return;
|
|
}
|
|
|
|
#ifdef WINNT
|
|
// Fetch the alternate color (for compression) if supplied.
|
|
|
|
{
|
|
DWORD cbData = sizeof(COLORREF);
|
|
DWORD dwType;
|
|
RegQueryValueEx(g_hkeyExplorer, c_szAltColor, NULL, &dwType, (LPBYTE)&g_crAltColor, &cbData);
|
|
}
|
|
#endif
|
|
|
|
if (RegOpenKey(g_hkeyExplorer, c_szCabinetStreams, &hkCabStreams) == ERROR_SUCCESS)
|
|
{
|
|
DEFFOLDERSETTINGS dfs;
|
|
DWORD cbData = SIZEOF(dfs);
|
|
DWORD dwType;
|
|
|
|
if (RegQueryValueEx(hkCabStreams, c_szSettings, NULL, &dwType, (LPBYTE)&dfs, &cbData) == ERROR_SUCCESS &&
|
|
dwType == REG_BINARY &&
|
|
cbData == SIZEOF(dfs)) {
|
|
|
|
// extra validation
|
|
if (dfs.uDefViewMode >= FVM_ICON &&
|
|
dfs.uDefViewMode <= FVM_DETAILS) {
|
|
g_dfs = dfs;
|
|
}
|
|
}
|
|
RegCloseKey(hkCabStreams);
|
|
}
|
|
}
|
|
|
|
// REVIEW UNDONE - Stuff in programs defaults to save positions ???
|
|
void _GetDefaultFolderSettings(PCABVIEW pcv)
|
|
{
|
|
HDC hdc;
|
|
|
|
// set the flags
|
|
|
|
// Best fit window means get the window to size according to the
|
|
// contents of the view so that windows without existing settings
|
|
// come up looking OK.
|
|
#ifdef WANT_MENUONOFF
|
|
pcv->wv.bMenuBar = TRUE;
|
|
#endif // WANT_MENUONOFF
|
|
#ifdef WANT_TABS
|
|
pcv->wv.bTabs = FALSE;
|
|
#endif // WANT_TABS
|
|
|
|
pcv->fs.fFlags = FWF_BESTFITWINDOW;
|
|
pcv->wv.bStatusBar = g_dfs.bDefStatusBar;
|
|
pcv->wv.bTree = FALSE;
|
|
|
|
if (g_CabState.fSimpleDefault && g_CabState.fNewWindowMode)
|
|
{
|
|
pcv->wv.bToolBar = g_dfs.bDefToolBarMulti;
|
|
}
|
|
else
|
|
{
|
|
pcv->wv.bToolBar = g_dfs.bDefToolBarSingle;
|
|
}
|
|
|
|
pcv->fs.ViewMode = g_dfs.uDefViewMode;
|
|
|
|
hdc = GetDC(NULL);
|
|
pcv->TreeSplit = GetDeviceCaps(hdc, LOGPIXELSY) * 2;
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
|
|
|
|
void _GetDefaultExplorerSettings(PCABVIEW pcv)
|
|
{
|
|
_GetDefaultFolderSettings(pcv);
|
|
|
|
// set the flags
|
|
pcv->fs.fFlags = 0;
|
|
|
|
pcv->fs.ViewMode = FVM_LIST;
|
|
|
|
pcv->wp.length = 0;
|
|
pcv->wHotkey = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Read the setting from a given stream.
|
|
// If the stream is null or seems invalid then this returns FALSE and uses
|
|
// some default settings.
|
|
BOOL Settings_ReadFromStream(LPSTREAM pstm, PCABVIEW pcv, UINT uFlags)
|
|
{
|
|
ULONG cbRead;
|
|
CABSH cabsh;
|
|
|
|
// for clean boot don't use saved settings
|
|
if (pstm && !g_fCleanBoot)
|
|
{
|
|
RECT rcWorkArea;
|
|
|
|
// Now read in the state from the stream file.
|
|
pstm->lpVtbl->Read(pstm, &cabsh, SIZEOF(cabsh), &cbRead);
|
|
|
|
// Sanity test to make sure we read in as many bytes as expected
|
|
if ((cbRead != (ULONG)SIZEOF(cabsh)) || (cbRead > cabsh.dwSize))
|
|
goto UseDefaultSettings;
|
|
|
|
// Now extract the data and put it into appropriate structures
|
|
|
|
// first the window placement info
|
|
pcv->wp.length = SIZEOF(pcv->wp);
|
|
pcv->wp.flags = (UINT)cabsh.flags;
|
|
pcv->wp.showCmd = (UINT)cabsh.showCmd;
|
|
pcv->wp.ptMinPosition.x = (int)cabsh.ptMinPosition.x;
|
|
pcv->wp.ptMinPosition.y = (int)cabsh.ptMinPosition.y;
|
|
pcv->wp.ptMaxPosition.x = (int)cabsh.ptMaxPosition.x;
|
|
pcv->wp.ptMaxPosition.y = (int)cabsh.ptMaxPosition.y;
|
|
|
|
pcv->wp.rcNormalPosition.left = (int)cabsh.rcNormalPosition.left;
|
|
pcv->wp.rcNormalPosition.right = (int)cabsh.rcNormalPosition.right;
|
|
pcv->wp.rcNormalPosition.top = (int)cabsh.rcNormalPosition.top;
|
|
pcv->wp.rcNormalPosition.bottom = (int)cabsh.rcNormalPosition.bottom;
|
|
|
|
// Do some simply sanity checks to make sure that the returned
|
|
// information appears to be reasonable and not random garbage
|
|
// We want the Show command to be normal or minimize or maximize.
|
|
// Only need one test as they are consectutive and start at zero
|
|
//
|
|
if ((pcv->wp.showCmd > SW_MAX) ||
|
|
IsRectEmpty(&pcv->wp.rcNormalPosition))
|
|
goto UseDefaultSettings;
|
|
|
|
// Make sure part of it will be visible.
|
|
|
|
SystemParametersInfo(SPI_GETWORKAREA, FALSE, &rcWorkArea, 0);
|
|
if (!IntersectRect(&rcWorkArea, &rcWorkArea,
|
|
&pcv->wp.rcNormalPosition))
|
|
goto UseDefaultSettings;
|
|
|
|
|
|
// Now the folder settings
|
|
pcv->fs.ViewMode = (UINT)cabsh.ViewMode;
|
|
pcv->fs.fFlags = (UINT)cabsh.fFlags;
|
|
pcv->TreeSplit = (UINT)cabsh.TreeSplit;
|
|
|
|
// And the Hotkey
|
|
pcv->wHotkey = (UINT)cabsh.dwHotkey;
|
|
|
|
pcv->wv = cabsh.wv;
|
|
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
UseDefaultSettings:
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("c.gsfp: No loading."));
|
|
#endif
|
|
if ((uFlags & COF_EXPLORE) || !g_CabState.fNewWindowMode)
|
|
{
|
|
_GetDefaultExplorerSettings(pcv);
|
|
return(FALSE);
|
|
}
|
|
|
|
_GetDefaultFolderSettings(pcv);
|
|
pcv->wp.length = 0;
|
|
pcv->wHotkey = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
BOOL Cabinet_GetStateFromPidl(LPCITEMIDLIST pidl, PCABVIEW pcv, UINT uFlags)
|
|
{
|
|
BOOL fRes = FALSE;
|
|
LPSTREAM pstm;
|
|
BOOL fOtherExplorer=FALSE;
|
|
|
|
// Get a stream for the given idlist.
|
|
if (uFlags & COF_EXPLORE)
|
|
{
|
|
// If there is another explorer window we need to remember this
|
|
// and clear out the window position stuff as to keep them from
|
|
// opening up over each other
|
|
// We have a custom place for explorer settings in the registry.
|
|
fOtherExplorer = (BOOL)FindWindow(c_szExploreClass, NULL);
|
|
pstm = OpenRegStream(g_hkeyExplorer, c_szCabinetExpView, c_szSettings, STGM_READ);
|
|
}
|
|
else
|
|
{
|
|
// fNewWindowMode and !fNewWindowMode try to get the window state
|
|
// from the same place. These modes differ on save behavior.
|
|
pstm = Cabinet_GetViewStreamForPidl(pidl, STGM_READ, c_szCabStreamInfo);
|
|
}
|
|
|
|
// Now read the setting from the stream.
|
|
fRes = Settings_ReadFromStream(pstm, pcv, uFlags);
|
|
// Everything was ok so release the stream.
|
|
if (pstm)
|
|
pstm->lpVtbl->Release(pstm);
|
|
|
|
if (fRes && fOtherExplorer)
|
|
{
|
|
// For now try simply clearing out the window placement from
|
|
// the restored state...
|
|
pcv->wp.length = 0;
|
|
}
|
|
|
|
// Also do some more validation here like we should not have the
|
|
// FWF_DESKTOP BIT Set!
|
|
if (pcv->fs.fFlags & FWF_DESKTOP)
|
|
{
|
|
Assert(FALSE); // Should only be set if we are the desktop...
|
|
pcv->fs.fFlags &= ~(FWF_DESKTOP);
|
|
}
|
|
|
|
return fRes;
|
|
}
|
|
|
|
|
|
void _InitTreeViewState(PFileCabinet pfc, HTREEITEM hti)
|
|
{
|
|
//
|
|
// #5812: Expand if the selected one is one of root objects.
|
|
//
|
|
if (TreeView_GetParent(pfc->hwndTree, hti) == NULL)
|
|
{
|
|
// REVIEW: Ask JoeB, if he really wants to have this feature.
|
|
TreeView_Expand(pfc->hwndTree, hti, TVE_EXPAND);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL InitCabinetClass(HINSTANCE hinst, LPCTSTR pszClass)
|
|
{
|
|
WNDCLASSEX wc;
|
|
WNDCLASS Oldwc;
|
|
|
|
wc.cbSize = SIZEOF(WNDCLASSEX);
|
|
|
|
if (GetClassInfoEx(hinst, pszClass, &wc))
|
|
return TRUE;
|
|
|
|
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
|
|
Oldwc.style = CS_DBLCLKS | CS_BYTEALIGNWINDOW;
|
|
Oldwc.lpfnWndProc = CabinetWndProc;
|
|
Oldwc.cbClsExtra = 0;
|
|
Oldwc.cbWndExtra = SIZEOF(PFileCabinet);
|
|
Oldwc.hInstance = hinst;
|
|
|
|
// pass in NULL here because we don't ever want user to shrink
|
|
// this down for us to make a small icon
|
|
// cause the current shrink code is ugly
|
|
Oldwc.hIcon = NULL; //g_hIconDefOpenLarge;
|
|
Oldwc.hCursor = LoadCursor(hinst, MAKEINTRESOURCE(CUR_SPLIT));
|
|
Oldwc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
|
|
Oldwc.lpszMenuName = NULL;
|
|
Oldwc.lpszClassName = pszClass;
|
|
|
|
if (RegisterClass(&Oldwc)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(GetClassInfo(hinst, pszClass, &Oldwc));
|
|
}
|
|
}
|
|
wc.cbSize = SIZEOF(WNDCLASSEX);
|
|
wc.style = CS_DBLCLKS | CS_BYTEALIGNWINDOW;
|
|
wc.lpfnWndProc = CabinetWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = SIZEOF(PFileCabinet);
|
|
wc.hInstance = hinst;
|
|
|
|
// pass in NULL here because we don't ever want user to shrink
|
|
// this down for us to make a small icon
|
|
// cause the current shrink code is ugly
|
|
wc.hIcon = NULL; //g_hIconDefOpenLarge;
|
|
wc.hCursor = LoadCursor(hinst, MAKEINTRESOURCE(CUR_SPLIT));
|
|
wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = pszClass;
|
|
wc.hIconSm = g_hIconDefOpenSmall;
|
|
|
|
if (RegisterClassEx(&wc))
|
|
return TRUE;
|
|
|
|
//
|
|
// Dealing with multi-thread race condition.
|
|
//
|
|
return GetClassInfoEx(hinst, pszClass, &wc);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Return TRUE if the given placement would put a window roughly in the
|
|
// work area. Otherwise return FALSE and optionally fill in appropriate
|
|
// offsets to make the window (totally) visible.
|
|
BOOL Placement_IsMostlyInWorkArea(LPWINDOWPLACEMENT pwp, int *px, int *py)
|
|
{
|
|
RECT rcWArea;
|
|
LPRECT prcNorm;
|
|
int slop;
|
|
int x, y;
|
|
|
|
x = y = 0;
|
|
SystemParametersInfo(SPI_GETWORKAREA, FALSE, &rcWArea, FALSE);
|
|
// Arbitrary slop value - defines how much of the window must be visible
|
|
// for it to be considered "mostly in work area".
|
|
slop = GetSystemMetrics(SM_CXICON);
|
|
InflateRect(&rcWArea, -slop, -slop);
|
|
|
|
prcNorm = &pwp->rcNormalPosition;
|
|
|
|
if (prcNorm->left > rcWArea.right)
|
|
x = rcWArea.right - prcNorm->right;
|
|
if (prcNorm->right < rcWArea.left)
|
|
x = rcWArea.left - prcNorm->left;
|
|
if (prcNorm->top > rcWArea.bottom)
|
|
y = rcWArea.bottom - prcNorm->bottom;
|
|
if (prcNorm->bottom < rcWArea.top)
|
|
y = rcWArea.top - prcNorm->top;
|
|
|
|
if (px)
|
|
*px = x;
|
|
if (py)
|
|
*py = y;
|
|
|
|
if (x || y)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Validate version of SetWindowPlacement
|
|
BOOL Window_SetPlacement(HWND hwnd, LPWINDOWPLACEMENT pwp)
|
|
{
|
|
int x, y;
|
|
|
|
// Make sure the window is at least kinda on the screen.
|
|
// NB We don't want to be too anal and not allow windows partially off screen.
|
|
if (!Placement_IsMostlyInWorkArea(pwp, &x, &y))
|
|
{
|
|
// Move window on screen.
|
|
OffsetRect(&pwp->rcNormalPosition, x, y);
|
|
}
|
|
|
|
// Set it.
|
|
return SetWindowPlacement(hwnd, pwp);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Create a folder window with the required tool windows and a view window
|
|
// if required.
|
|
// if (lpwp->length == 0), the windowplacement is not initialized. this
|
|
// is needed for the first folder that may or may not have a local view.
|
|
//---------------------------------------------------------------------------
|
|
HRESULT Cabinet_CreateWindow(PCABVIEW pcv,
|
|
LPCITEMIDLIST pidl,
|
|
UINT nCmdShow,
|
|
HWND *phwnd)
|
|
{
|
|
HRESULT hres = ResultFromScode(E_OUTOFMEMORY); // assume error
|
|
HWND hwnd;
|
|
PFileCabinet pfc;
|
|
LPCTSTR pszClass;
|
|
|
|
*phwnd = NULL; // assume error
|
|
|
|
// Create toplevel cabinet app window...
|
|
|
|
pszClass = pcv->wv.bTree ? c_szExploreClass : c_szCabinetClass;
|
|
|
|
if (!InitCabinetClass(hinstCabinet, pszClass))
|
|
{
|
|
Assert(hres==ResultFromScode(E_OUTOFMEMORY));
|
|
DEBUG_BREAK;
|
|
return hres;
|
|
}
|
|
|
|
hwnd = CreateWindowEx(WS_EX_WINDOWEDGE, pszClass, NULL,
|
|
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
NULL, NULL, hinstCabinet, pcv);
|
|
|
|
|
|
if (hwnd)
|
|
{
|
|
HTREEITEM hti = NULL;
|
|
LPOneTreeNode lpnd;
|
|
|
|
//
|
|
// Make this window foreground.
|
|
//
|
|
#ifdef BOBDAY_TESTING_FOCUS_CHANGES
|
|
SetActiveWindow(hwnd);
|
|
#else
|
|
SetForegroundWindow(hwnd);
|
|
#endif
|
|
|
|
// Restore its properties...
|
|
|
|
pfc = GetPFC(hwnd);
|
|
|
|
if (pcv->wp.length)
|
|
{
|
|
// Don't really show the window yet
|
|
pcv->wp.showCmd = SW_HIDE;
|
|
pcv->wp.length = SIZEOF(pcv->wp);
|
|
|
|
// we do a SetWindowPlacement first with SW_HIDE
|
|
// to get the size so we can do SetWindowStates.
|
|
// then we really show it.
|
|
Window_SetPlacement(hwnd, &pcv->wp);
|
|
}
|
|
|
|
SetWindowStates(pfc);
|
|
// Don't show the window yet if we're probably going to size
|
|
// it later.
|
|
if (!(pcv->fs.fFlags & FWF_BESTFITWINDOW))
|
|
if (!IsWindowVisible(hwnd)) {
|
|
ShowWindow(hwnd, nCmdShow);
|
|
UpdateWindow(hwnd);
|
|
}
|
|
|
|
lpnd = OTGetNodeFromIDListEx(pidl, OTGNF_TRYADD, &hres);
|
|
|
|
if (lpnd)
|
|
{
|
|
Assert(SUCCEEDED(hres));
|
|
|
|
// Create a view window of the appropriate type,
|
|
// (based on the path). We do this after showing
|
|
// the window to reduce the amount of time the user
|
|
// spends looking at a blank screen.
|
|
//
|
|
pfc->fs = pcv->fs;
|
|
|
|
//
|
|
// Cabinet_ChangeView may return S_FALSE, if it failed to
|
|
// change the view. In this context, we should treat it as
|
|
// an error. That's why we don't use SUCCEEDED macro here.
|
|
//
|
|
hres = Cabinet_ChangeView(pfc, lpnd, pidl, TRUE);
|
|
if (hres==NOERROR)
|
|
{
|
|
// Initialize tree window's visual states
|
|
if (pfc->hwndTree) {
|
|
hti = Tree_Build(pfc, pidl, TRUE, TRUE);
|
|
// BUGBUG do more than this assert..
|
|
Assert(lpnd == Tree_GetFCTreeData(pfc->hwndTree, hti));
|
|
|
|
if (hti == NULL)
|
|
hti = TreeView_GetSelection(pfc->hwndTree);
|
|
if (hti) _InitTreeViewState(pfc, hti);
|
|
}
|
|
|
|
// We *may* not have shown it yet so make sure now.
|
|
if (!IsWindowVisible(hwnd))
|
|
ShowWindow(hwnd, nCmdShow);
|
|
|
|
// Set the focus to the view window.
|
|
SetFocus(pfc->hwndView);
|
|
|
|
*phwnd = hwnd;
|
|
return NOERROR; // success
|
|
}
|
|
|
|
if (hres==E_OUTOFMEMORY) {
|
|
DEBUG_BREAK;
|
|
}
|
|
|
|
OTRelease(lpnd);
|
|
}
|
|
else
|
|
{
|
|
Assert(FAILED(hres));
|
|
DebugMsg(DM_TRACE, TEXT("ca ER - Cabinet_CreateWindow: OTGetNodeFromIDList failed (%x)"), hres);
|
|
DEBUG_BREAK;
|
|
}
|
|
|
|
DestroyWindow(hwnd);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_BREAK;
|
|
DebugMsg(DM_TRACE, TEXT("ca ER - Cabinet_CreateWindow: CreateWindow failed"));
|
|
Assert(hres==ResultFromScode(E_OUTOFMEMORY));
|
|
}
|
|
|
|
Assert(FAILED(hres));
|
|
Assert(*phwnd==NULL);
|
|
|
|
DebugMsg(DM_ERROR, TEXT("c.cfw: Unable to create a main Cabinet window."));
|
|
|
|
return hres; // failure
|
|
}
|
|
|
|
|
|
#if 0
|
|
void GetSetupData(LPTSTR pszPath, int cbPath, DWORD *pFlags)
|
|
{
|
|
HKEY hkey;
|
|
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP, &hkey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD cbData;
|
|
|
|
if (pszPath)
|
|
{
|
|
*pszPath = 0;
|
|
cbData = cbPath;
|
|
RegQueryValueEx(hkey, REGSTR_VAL_OLDWINDIR, NULL, NULL, (LPVOID)pszPath, &cbData);
|
|
DebugMsg(DM_TRACE, TEXT("OldSetupPath %s"), pszPath);
|
|
}
|
|
|
|
if (pFlags)
|
|
{
|
|
*pFlags = 0;
|
|
cbData = SIZEOF(*pFlags);
|
|
RegQueryValueEx(hkey, REGSTR_VAL_SETUPFLAGS, NULL, NULL, (LPVOID)pFlags, &cbData);
|
|
DebugMsg(DM_TRACE, TEXT("SetupFlags %x"), *pFlags);
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
typedef enum {
|
|
RRA_DEFAULT = 0x0000,
|
|
RRA_DELETE = 0x0001,
|
|
RRA_WAIT = 0x0002,
|
|
} RRA_FLAGS;
|
|
|
|
BOOL RunRegApps(HKEY hkeyParent, LPCTSTR szSubkey, RRA_FLAGS fFlags)
|
|
{
|
|
HKEY hkey;
|
|
BOOL fShellInit = FALSE;
|
|
HCURSOR hcurSave;
|
|
|
|
|
|
if (RegOpenKey(hkeyParent, szSubkey, &hkey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD cbData, cbValue, dwType, i;
|
|
TCHAR szValueName[32], szCmdLine[MAX_PATH];
|
|
STARTUPINFO startup;
|
|
PROCESS_INFORMATION pi;
|
|
BOOL fUpdatedDesktopCursor = FALSE;
|
|
|
|
startup.cb = SIZEOF(startup);
|
|
startup.lpReserved = NULL;
|
|
startup.lpDesktop = NULL;
|
|
startup.lpTitle = NULL;
|
|
startup.dwFlags = 0L;
|
|
startup.cbReserved2 = 0;
|
|
startup.lpReserved2 = NULL;
|
|
//startup.wShowWindow = wShowWindow;
|
|
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
LONG lEnum;
|
|
|
|
cbValue = ARRAYSIZE(szValueName);
|
|
cbData = SIZEOF(szCmdLine);
|
|
|
|
if (!fUpdatedDesktopCursor && (fFlags & RRA_WAIT))
|
|
{
|
|
fUpdatedDesktopCursor = TRUE;
|
|
hcurSave = (HCURSOR)SetClassLong(GetDesktopWindow(), GCL_HCURSOR,
|
|
(LONG)LoadCursor(NULL, IDC_WAIT));
|
|
}
|
|
|
|
// BUGBUG (Unicode, Davepl) I'm assuming that the data is UNICODE,
|
|
// but I'm not sure who put it there yet... double check.
|
|
|
|
if( ( lEnum = RegEnumValue( hkey, i, szValueName, &cbValue, NULL,
|
|
&dwType, (LPBYTE) szCmdLine, &cbData ) ) == ERROR_MORE_DATA )
|
|
{
|
|
// ERROR_MORE_DATA means the value name or data was too large
|
|
// skip to the next item
|
|
DebugMsg( DM_ERROR, TEXT("Cannot run oversize entry in <%s>"), szSubkey );
|
|
continue;
|
|
}
|
|
else if( lEnum != ERROR_SUCCESS )
|
|
{
|
|
// could be ERROR_NO_MORE_ENTRIES, or some kind of failure
|
|
// we can't recover from any other registry problem, anyway
|
|
break;
|
|
}
|
|
|
|
if (dwType == REG_SZ)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("%s %s"), szSubkey, szCmdLine);
|
|
// only run things marked with a "*" in clean boot
|
|
if (g_fCleanBoot && (szValueName[0] != TEXT('*')))
|
|
continue;
|
|
|
|
// NB Things marked with a '!' mean delete after
|
|
// the CreateProcess not before. This is to allow
|
|
// certain apps (runonce.exe) to be allowed to rerun
|
|
// to if the machine goes down in the middle of execing
|
|
// them. Be very afraid of this switch.
|
|
|
|
if ((fFlags & RRA_DELETE) && (szValueName[0] != TEXT('!'))) {
|
|
// This delete can fail if the user doesn't have the privilege
|
|
if (RegDeleteValue(hkey, szValueName) == ERROR_SUCCESS)
|
|
{
|
|
// adjust for shift in value index only if delete succeeded
|
|
i--;
|
|
}
|
|
}
|
|
|
|
if (lstrcmpi(szValueName, TEXT("InitShell")) == 0)
|
|
{
|
|
fShellInit = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We used to call CreateProcess( NULL, szCmdLine, ...) here,
|
|
// but thats not useful for people with apppaths stuff.
|
|
//
|
|
LPTSTR lpszArgs;
|
|
SHELLEXECUTEINFO ExecInfo;
|
|
|
|
PathProcessCommand(szCmdLine,
|
|
szCmdLine, ARRAYSIZE(szCmdLine),
|
|
PPCF_ADDARGUMENTS | PPCF_FORCEQUALIFY);
|
|
|
|
lpszArgs = PathGetArgs(szCmdLine);
|
|
if (*lpszArgs)
|
|
*(lpszArgs-1) = TEXT('\0'); // Strip args
|
|
|
|
PathUnquoteSpaces(szCmdLine);
|
|
|
|
FillExecInfo(ExecInfo, NULL, NULL, szCmdLine, lpszArgs, NULL, SW_SHOWNORMAL);
|
|
ExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS;
|
|
if (ShellExecuteEx(&ExecInfo))
|
|
{
|
|
if ((fFlags & RRA_WAIT) && ExecInfo.hProcess != NULL)
|
|
{
|
|
MsgWaitForMultipleObjectsLoop(ExecInfo.hProcess, INFINITE);
|
|
}
|
|
CloseHandle(ExecInfo.hProcess);
|
|
}
|
|
|
|
// Post delete '!' things.
|
|
if ((fFlags & RRA_DELETE) && (szValueName[0] == TEXT('!'))) {
|
|
// This delete can fail if the user doesn't have the privilege
|
|
if (RegDeleteValue(hkey, szValueName) == ERROR_SUCCESS)
|
|
{
|
|
// adjust for shift in value index only if delete succeeded
|
|
i--; // adjust for shift in value index
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hkey);
|
|
|
|
if (fUpdatedDesktopCursor)
|
|
{
|
|
SetClassLong(GetDesktopWindow(), GCL_HCURSOR, (LONG)hcurSave);
|
|
}
|
|
}
|
|
|
|
return(fShellInit);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreateShellDirectories()
|
|
{
|
|
// Create the shell directories if they don't exist
|
|
|
|
ILFree(SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE));
|
|
ILFree(SHCloneSpecialIDList(NULL, CSIDL_PROGRAMS, TRUE));
|
|
ILFree(SHCloneSpecialIDList(NULL, CSIDL_STARTMENU, TRUE));
|
|
ILFree(SHCloneSpecialIDList(NULL, CSIDL_STARTUP, TRUE));
|
|
ILFree(SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE));
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns:
|
|
// TRUE if the user wants to abort the startup sequence
|
|
// FALSE keep going
|
|
//
|
|
// note: this is a switch, once on it will return TRUE to all
|
|
// calls so these keys don't need to be pressed the whole time
|
|
|
|
BOOL AbortStartup()
|
|
{
|
|
static BOOL bAborted = FALSE; // static so it sticks!
|
|
|
|
// DebugMsg(DM_TRACE, "Abort Startup?");
|
|
|
|
if (bAborted)
|
|
return TRUE; // don't do funky startup stuff
|
|
else {
|
|
bAborted = (g_fCleanBoot || ((GetAsyncKeyState(VK_CONTROL) < 0) || (GetAsyncKeyState(VK_SHIFT) < 0)));
|
|
return bAborted;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
BOOL EnumFolder_Startup(LPSHELLFOLDER psf, HWND hwndOwner, LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
|
|
{
|
|
LPCONTEXTMENU pcm;
|
|
HRESULT hres;
|
|
MSG msg;
|
|
|
|
hres = psf->lpVtbl->GetUIObjectOf(psf, hwndOwner, 1, &pidlItem, &IID_IContextMenu, NULL, &pcm);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
HMENU hmenu = CreatePopupMenu();
|
|
if (hmenu)
|
|
{
|
|
#define CMD_ID_FIRST 1
|
|
#define CMD_ID_LAST 0x7fff
|
|
INT idCmd;
|
|
pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, CMD_ID_FIRST, CMD_ID_LAST, CMF_DEFAULTONLY);
|
|
idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
|
|
if (idCmd)
|
|
{
|
|
CMINVOKECOMMANDINFOEX ici = {
|
|
SIZEOF(CMINVOKECOMMANDINFOEX),
|
|
0L,
|
|
hwndOwner,
|
|
(LPSTR)MAKEINTRESOURCE(idCmd - 1),
|
|
NULL, NULL,
|
|
SW_NORMAL,
|
|
};
|
|
|
|
pcm->lpVtbl->InvokeCommand(pcm,
|
|
(LPCMINVOKECOMMANDINFO)&ici);
|
|
|
|
}
|
|
DestroyMenu(hmenu);
|
|
}
|
|
pcm->lpVtbl->Release(pcm);
|
|
}
|
|
|
|
if (AbortStartup())
|
|
return FALSE;
|
|
|
|
// This is a semi hack but we want to process any of the messages
|
|
// we get back from the hook for any new windows we create as to
|
|
// not overlflow our maximum limit... We know that it is a
|
|
// registered message so only process those messages...
|
|
while (PeekMessage(&msg, NULL, 0xc000, 0xffff, PM_REMOVE))
|
|
{
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void EnumFolder(HWND hwndOwner, LPITEMIDLIST pidlFolder, DWORD grfFlags, PFNENUMFOLDERCALLBACK pfn)
|
|
{
|
|
HRESULT hres;
|
|
LPSHELLFOLDER psf;
|
|
|
|
hres = s_pshfRoot->lpVtbl->BindToObject(s_pshfRoot, pidlFolder, NULL, &IID_IShellFolder, &psf);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
LPENUMIDLIST penum;
|
|
hres = psf->lpVtbl->EnumObjects(psf, hwndOwner, grfFlags, &penum);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
UINT celt;
|
|
while (penum->lpVtbl->Next(penum, 1, &pidl, &celt)==NOERROR && celt==1)
|
|
{
|
|
if (!(*pfn)(psf, hwndOwner, pidlFolder, pidl))
|
|
{
|
|
SHFree(pidl);
|
|
break;
|
|
}
|
|
SHFree(pidl);
|
|
}
|
|
penum->lpVtbl->Release(penum);
|
|
}
|
|
psf->lpVtbl->Release(psf);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void _ExecuteStartupPrograms(HWND hwndOwner)
|
|
{
|
|
LPITEMIDLIST pidlStartup;
|
|
|
|
if (AbortStartup())
|
|
return;
|
|
|
|
pidlStartup = SHCloneSpecialIDList(NULL, CSIDL_COMMON_STARTUP, TRUE);
|
|
if (pidlStartup)
|
|
{
|
|
EnumFolder(hwndOwner, pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, EnumFolder_Startup);
|
|
ILFree(pidlStartup);
|
|
}
|
|
|
|
|
|
pidlStartup = SHCloneSpecialIDList(NULL, CSIDL_STARTUP, TRUE);
|
|
if (pidlStartup)
|
|
{
|
|
EnumFolder(hwndOwner, pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, EnumFolder_Startup);
|
|
ILFree(pidlStartup);
|
|
}
|
|
}
|
|
|
|
|
|
// BUGBUG:: A bunch of this code can get reduced down... ie boiled
|
|
const LPTSTR c_aszForceCheckWinIni[] = {TEXT("GROUPS.B$$"), NULL};
|
|
|
|
void CheckWinIniForAssocs(void);
|
|
|
|
void BoilThatDustSpec(LPTSTR pStart, int nCmdShow)
|
|
{
|
|
LPTSTR pEnd;
|
|
BOOL bFinished;
|
|
const LPTSTR *ppszForce;
|
|
TCHAR szFile[MAX_PATH];
|
|
LPTSTR pszFile;
|
|
SHELLEXECUTEINFO ExecInfo;
|
|
|
|
bFinished = FALSE;
|
|
while (!bFinished && !AbortStartup())
|
|
{
|
|
pEnd = pStart;
|
|
while ((*pEnd) && (*pEnd != TEXT(' ')) && (*pEnd != TEXT(',')))
|
|
pEnd = (LPTSTR)OFFSETOF(CharNext(pEnd));
|
|
|
|
if (*pEnd == 0)
|
|
bFinished = TRUE;
|
|
else
|
|
*pEnd = 0;
|
|
|
|
if (lstrlen(pStart) != 0)
|
|
{
|
|
// Load and Run lines are done relative to windows directory.
|
|
GetWindowsDirectory(szFile, ARRAYSIZE(szFile));
|
|
SetCurrentDirectory(szFile);
|
|
|
|
pszFile = PathFindFileName(pStart);
|
|
lstrcpy(szFile, pszFile);
|
|
PathRemoveFileSpec(pStart);
|
|
|
|
// App hacks to get borlands Setup program to work
|
|
for (ppszForce = c_aszForceCheckWinIni; *ppszForce; ppszForce++)
|
|
{
|
|
if (lstrcmpi(szFile, *ppszForce) == 0)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("c.boil: Apphack %s force winini scan"), szFile);
|
|
|
|
CheckWinIniForAssocs();
|
|
break;
|
|
}
|
|
}
|
|
|
|
FillExecInfo(ExecInfo, NULL, NULL, szFile, NULL, pStart, nCmdShow);
|
|
if (!ShellExecuteEx(&ExecInfo))
|
|
{
|
|
// BUGBUG: Should probably map error codes...
|
|
ShellMessageBox(hinstCabinet, NULL, MAKEINTRESOURCE(IDS_WINININORUN),
|
|
MAKEINTRESOURCE(IDS_DESKTOP),
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL,
|
|
(LPTSTR)szFile);
|
|
}
|
|
}
|
|
pStart = pEnd+1;
|
|
}
|
|
}
|
|
|
|
const TCHAR szRun[] = TEXT("Run");
|
|
const TCHAR szLoad[] = TEXT("Load");
|
|
|
|
void _DoRunEquals()
|
|
{
|
|
TCHAR szBuffer[255]; // max size of load= run= lines...
|
|
|
|
if (g_fCleanBoot)
|
|
return;
|
|
|
|
/* "Load" apps before "Run"ning any. */
|
|
GetProfileString(c_szWindows, szLoad, c_szNULL, szBuffer, ARRAYSIZE(szBuffer));
|
|
if (*szBuffer)
|
|
BoilThatDustSpec(szBuffer, SW_SHOWMINNOACTIVE);
|
|
|
|
GetProfileString(c_szWindows, szRun, c_szNULL, szBuffer, ARRAYSIZE(szBuffer));
|
|
if (*szBuffer)
|
|
BoilThatDustSpec(szBuffer, SW_SHOWNORMAL);
|
|
|
|
RunRegApps(HKEY_LOCAL_MACHINE, c_szRegRunKey, RRA_DEFAULT);
|
|
|
|
RunRegApps(HKEY_CURRENT_USER, c_szRegRunKey, RRA_DEFAULT);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Helper Function to see if a pidl is on a network drive which is not
|
|
// persistent. This is useful if we are shuting down and saving a list
|
|
// of the open windows to restore as we won't be able to restore these.
|
|
#define AnsiUpperChar(c) ( (TCHAR)LOWORD( (DWORD) CharUpper((LPTSTR)MAKELONG(c, 0))) )
|
|
|
|
BOOL FPidlOnNonPersistentDrive(LPCITEMIDLIST pidl)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
HANDLE hEnum;
|
|
BOOL fRet = TRUE;
|
|
|
|
if (!SHGetPathFromIDList(pidl, szPath) || (szPath[0] == TEXT('\0')))
|
|
return(FALSE); // not file system pidl assume ok.
|
|
|
|
if (PathIsUNC(szPath) || !IsNetDrive(DRIVEID(szPath)))
|
|
{
|
|
fRet = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
// Ok we got here so now we have a network drive ...
|
|
// we will have to enumerate over
|
|
//
|
|
if (WNetOpenEnum(RESOURCE_REMEMBERED, RESOURCETYPE_DISK,
|
|
RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED,
|
|
NULL, &hEnum) == WN_SUCCESS)
|
|
{
|
|
DWORD dwCount=1;
|
|
union
|
|
{
|
|
NETRESOURCE nr; // Large stack usage but I
|
|
TCHAR buf[1024]; // Dont think it is thunk to 16 bits...
|
|
}nrb;
|
|
|
|
DWORD dwBufSize = SIZEOF(nrb);
|
|
|
|
while (WNetEnumResource(hEnum, &dwCount, &nrb.buf,
|
|
&dwBufSize) == WN_SUCCESS)
|
|
{
|
|
// We only want to add items if they do not have a local
|
|
// name. If they had a local name we would have already
|
|
// added them!
|
|
if ((nrb.nr.lpLocalName != NULL) &&
|
|
(AnsiUpperChar(*(nrb.nr.lpLocalName)) == AnsiUpperChar(szPath[0])))
|
|
{
|
|
fRet = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
WNetCloseEnum(hEnum);
|
|
}
|
|
|
|
End:
|
|
DebugMsg(DM_TRACE, TEXT("c.c_arl: %s, is Persistent? %d"), szPath, fRet);
|
|
return(fRet);
|
|
|
|
|
|
}
|
|
|
|
|
|
const TCHAR c_szOpenIDL[] = TEXT("/idlist,:%ld:%ld,/root,/idlist,:%ld:%ld");
|
|
const TCHAR c_szExploreIDL[] = TEXT(",/e");
|
|
const TCHAR c_szTemplateD[] = TEXT("%d");
|
|
|
|
const TCHAR c_szComma[] = TEXT(",");
|
|
const TCHAR c_szIDListParam[] = TEXT("%s,:%ld:%ld");
|
|
|
|
// Increment this when the saved structure changes
|
|
const USHORT c_uVersion = 0x8001;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Restore all of the window that asked to save a command line to be
|
|
// restarted when windows was exited.
|
|
//
|
|
|
|
BOOL AddCabinetToRestartList(UINT flags, LPCITEMIDLIST pidl)
|
|
{
|
|
// See if this is the first one...
|
|
int cItems;
|
|
TCHAR szSubKey[80];
|
|
BOOL fRet = FALSE;
|
|
DWORD cbData;
|
|
USHORT us;
|
|
LPSTREAM pstm;
|
|
HKEY hkeyExplorer;
|
|
|
|
// If in clean boot mode don't save away the list.
|
|
if (g_fCleanBoot)
|
|
return(FALSE);
|
|
|
|
if (FPidlOnNonPersistentDrive(pidl))
|
|
return FALSE;
|
|
|
|
if (RegCreateKey(HKEY_CURRENT_USER, c_szRegExplorer, &hkeyExplorer) != ERROR_SUCCESS)
|
|
{
|
|
Assert (FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
cbData = SIZEOF(cItems);
|
|
if (!Reg_GetStruct(hkeyExplorer, c_szSaveCmds, c_szCount, &cItems, &cbData))
|
|
cItems = 0;
|
|
|
|
// Now Lets Create a registry Stream for this guy...
|
|
wsprintf(szSubKey, c_szTemplateD, cItems);
|
|
pstm = OpenRegStream(hkeyExplorer, c_szSaveCmds, szSubKey, STGM_WRITE);
|
|
if (pstm)
|
|
{
|
|
LPCITEMIDLIST pidlRoot;
|
|
ITEMIDLIST idl = {0};
|
|
|
|
// Now Write a preamble to the start of the line that
|
|
// tells us that this is an explorer
|
|
us = (USHORT)-1; // SIZEOF of cmd line == -1 implies pidl...
|
|
pstm->lpVtbl->Write(pstm, &us, SIZEOF(us), NULL);
|
|
|
|
// Now Write out the version number of this stream
|
|
// Make sure to inc the version number if the structure changes
|
|
pstm->lpVtbl->Write(pstm, &c_uVersion, SIZEOF(c_uVersion), NULL);
|
|
|
|
// Now Write out the flags
|
|
pstm->lpVtbl->Write(pstm, &flags, SIZEOF(flags), NULL);
|
|
|
|
pidlRoot = Desktop_GetRootPidl();
|
|
if (!pidlRoot)
|
|
{
|
|
pidlRoot = &idl;
|
|
}
|
|
|
|
// And the root pidl;
|
|
ILSaveToStream(pstm, pidlRoot);
|
|
|
|
// And the pidl;
|
|
ILSaveToStream(pstm, pidl);
|
|
|
|
// And Release the stream;
|
|
pstm->lpVtbl->Release(pstm);
|
|
|
|
cItems++; // Say that there are twice as many items...
|
|
|
|
fRet = Reg_SetStruct(hkeyExplorer, c_szSaveCmds, c_szCount, &cItems, SIZEOF(cItems));
|
|
}
|
|
|
|
RegCloseKey(hkeyExplorer);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Some helper functions to manage a list of the current folders that are
|
|
// opening in the current process. This will be used internal to this
|
|
// file to try to keep from opening multiple windows on the same pidl
|
|
//---------------------------------------------------------------------------
|
|
|
|
HDPA g_hdpaOpeningFolders = NULL;
|
|
|
|
int OFLInList(LPCITEMIDLIST pidl)
|
|
{
|
|
int iRet;
|
|
if (g_hdpaOpeningFolders == NULL)
|
|
return -1; // No dpa so no one to process...
|
|
|
|
ENTERCRITICAL;
|
|
for (iRet = DPA_GetPtrCount(g_hdpaOpeningFolders) -1; iRet >= 0; iRet--)
|
|
{
|
|
if (ILIsEqual(pidl,
|
|
(LPITEMIDLIST)DPA_FastGetPtr(g_hdpaOpeningFolders, iRet)))
|
|
break;
|
|
}
|
|
|
|
LEAVECRITICAL;
|
|
|
|
return iRet;
|
|
}
|
|
|
|
BOOL OFLFindOrAdd(LPCITEMIDLIST pidl)
|
|
{
|
|
LPITEMIDLIST pidlAdd;
|
|
int iRet;
|
|
|
|
pidlAdd = ILClone(pidl);
|
|
if (!pidlAdd)
|
|
return FALSE;
|
|
|
|
ENTERCRITICAL;
|
|
|
|
if (g_hdpaOpeningFolders == NULL)
|
|
g_hdpaOpeningFolders = DPA_Create(0);
|
|
|
|
for (iRet = DPA_GetPtrCount(g_hdpaOpeningFolders) -1; iRet >= 0; iRet--)
|
|
{
|
|
if (ILIsEqual(pidl,
|
|
(LPITEMIDLIST)DPA_FastGetPtr(g_hdpaOpeningFolders, iRet)))
|
|
{
|
|
ILFree(pidlAdd);
|
|
LEAVECRITICAL;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (g_hdpaOpeningFolders != NULL)
|
|
DPA_InsertPtr(g_hdpaOpeningFolders, 32767, pidlAdd);
|
|
else
|
|
ILFree(pidlAdd);
|
|
|
|
LEAVECRITICAL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void OFLRemove(LPCITEMIDLIST pidl)
|
|
{
|
|
int i;
|
|
|
|
ENTERCRITICAL;
|
|
i = OFLInList(pidl);
|
|
|
|
if (i >= 0)
|
|
{
|
|
ILFree((LPITEMIDLIST)DPA_FastGetPtr(g_hdpaOpeningFolders, i));
|
|
DPA_DeletePtr(g_hdpaOpeningFolders, i);
|
|
}
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void _CreateSavedWindows(void)
|
|
{
|
|
// See if this is the first one...
|
|
int cItems;
|
|
TCHAR szName[80];
|
|
DWORD cbData;
|
|
TCHAR szCmdLine[MAX_PATH+1]; // +1 for the first character of c_szShellExecute
|
|
|
|
|
|
cbData = SIZEOF(cItems);
|
|
if (!Reg_GetStruct(g_hkeyExplorer, c_szSaveCmds, c_szCount, &cItems, &cbData)) {
|
|
return;
|
|
}
|
|
|
|
if (cItems == 0)
|
|
return; // nothing to do
|
|
|
|
// Restart in the reverse order that they were added.
|
|
while (cItems--)
|
|
{
|
|
LPSTREAM pstm;
|
|
USHORT us;
|
|
MSG msg;
|
|
|
|
wsprintf(szName, c_szTemplateD, cItems);
|
|
if (AbortStartup())
|
|
break;
|
|
|
|
// This is a semi hack but we want to process any of the messages
|
|
// we get back from the hook for any new windows we create as to
|
|
// not overlflow our maximum limit... We know that it is a
|
|
// registered message so only process those messages...
|
|
while (PeekMessage(&msg, NULL, 0xc000, 0xffff, PM_REMOVE))
|
|
{
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
pstm = OpenRegStream(g_hkeyExplorer, c_szSaveCmds, szName, STGM_READ);
|
|
if (pstm)
|
|
{
|
|
// Now Write a preamble to the start of the line that
|
|
// tells us that this is an explorer
|
|
if (FAILED(pstm->lpVtbl->Read(pstm, &us, SIZEOF(us), NULL)))
|
|
goto Error1; // try the next one...
|
|
|
|
if (us == (USHORT)-1)
|
|
{
|
|
UINT flags;
|
|
LPITEMIDLIST pidl;
|
|
LPITEMIDLIST pidlRoot;
|
|
USHORT version;
|
|
|
|
// We have a folder serialized so get version, flags, pidlRoot,
|
|
// and pidl;
|
|
// Now Read the version
|
|
if (FAILED(pstm->lpVtbl->Read(pstm, &version, SIZEOF(version), NULL))
|
|
|| version != c_uVersion)
|
|
{
|
|
goto Error1;
|
|
}
|
|
|
|
// Now Write out the flags
|
|
pstm->lpVtbl->Read(pstm, &flags, SIZEOF(flags), NULL);
|
|
|
|
// And the root pidl;
|
|
pidl = NULL; // Load will try to free non null values...
|
|
if (FAILED(ILLoadFromStream(pstm, &pidl)) || (pidl == NULL))
|
|
goto Error1;
|
|
|
|
if (ILIsEmpty(pidl))
|
|
{
|
|
pidlRoot = NULL;
|
|
}
|
|
else
|
|
{
|
|
pidlRoot = ILGlobalClone(pidl);
|
|
if (!pidlRoot)
|
|
{
|
|
goto Error2;
|
|
}
|
|
}
|
|
ILFree(pidl);
|
|
|
|
// And the pidl;
|
|
pidl = NULL; // Load will try to free non null values...
|
|
if (FAILED(ILLoadFromStream(pstm, &pidl)) || (pidl == NULL))
|
|
goto Error2;
|
|
|
|
if (pidlRoot)
|
|
{
|
|
SHELLEXECUTEINFO ExecInfo;
|
|
TCHAR szExplorer[MAX_PATH];
|
|
HANDLE hIdList;
|
|
HANDLE hIdListRoot;
|
|
|
|
hIdList = SHAllocShared(pidl,ILGetSize(pidl),GetCurrentProcessId());
|
|
hIdListRoot = SHAllocShared(pidlRoot,ILGetSize(pidlRoot),GetCurrentProcessId());
|
|
|
|
if (hIdList && hIdListRoot)
|
|
{
|
|
|
|
// This should not fail
|
|
GetModuleFileName(NULL, szExplorer, ARRAYSIZE(szExplorer));
|
|
|
|
// We need a new instance of the Cabinet; this may be a
|
|
// little slow, but what can I do?
|
|
|
|
wsprintf(szCmdLine, c_szOpenIDL, hIdList, GetCurrentProcessId(),
|
|
hIdListRoot, GetCurrentProcessId());
|
|
|
|
if (flags & COF_EXPLORE)
|
|
{
|
|
lstrcat(szCmdLine, c_szExploreIDL);
|
|
}
|
|
|
|
FillExecInfo(ExecInfo, NULL, NULL, szExplorer, szCmdLine,
|
|
NULL, SW_SHOWNORMAL);
|
|
if (ShellExecuteEx(&ExecInfo))
|
|
{
|
|
// These are now owned by the new instance
|
|
hIdList = NULL;
|
|
hIdListRoot = NULL;
|
|
}
|
|
}
|
|
|
|
if (hIdList)
|
|
SHFreeShared(hIdList,GetCurrentProcessId());
|
|
if (hIdListRoot)
|
|
SHFreeShared(hIdListRoot,GetCurrentProcessId());
|
|
|
|
}
|
|
else
|
|
{
|
|
// If we are a folder and there is already a folder open
|
|
// on this pidl, blow it off, as we need to handle the case
|
|
// where a user puts a link to a folder in the startup group
|
|
// which opens fine the first time, but if they don't close
|
|
// it we will restore that window and also have the window
|
|
// from the startup group.
|
|
//
|
|
if ((flags & COF_EXPLORE) ||
|
|
((OFLInList(pidl) < 0) && (Cabinet_FindByPidl(pidl) == NULL)))
|
|
{
|
|
// hotkey is preserved in the save stream.
|
|
NEWFOLDERINFO fi;
|
|
|
|
fi.hwndCaller = v_hwndDesktop;
|
|
fi.pidl = pidl;
|
|
fi.uFlags = flags;
|
|
fi.nShow = SW_SHOWDEFAULT;
|
|
fi.dwHotKey = 0L;
|
|
|
|
Cabinet_OpenFolder(&fi);
|
|
}
|
|
}
|
|
|
|
Error2:
|
|
if (pidlRoot)
|
|
{
|
|
ILGlobalFree(pidlRoot);
|
|
}
|
|
if (pidl)
|
|
{
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
else if (us < MAX_PATH)
|
|
{
|
|
CHAR aszScratch[MAX_PATH];
|
|
pstm->lpVtbl->Read(pstm, aszScratch, us, NULL);
|
|
WinExec(aszScratch, SW_SHOWNORMAL); // the show cmd will be ignored
|
|
}
|
|
|
|
Error1:
|
|
pstm->lpVtbl->Release(pstm);
|
|
}
|
|
}
|
|
|
|
SHRegDeleteKey(g_hkeyExplorer, c_szSaveCmds);
|
|
}
|
|
|
|
|
|
//
|
|
// This function destroys pfc->hwndView (if any) and releases pfc->pidl,
|
|
// pfc->lpndOpen and pfc->psv.
|
|
//
|
|
VOID Cabinet_ReleaseShellView(PFileCabinet pfc)
|
|
{
|
|
if (pfc->psv)
|
|
{
|
|
if (pfc->hwndView)
|
|
{
|
|
//
|
|
// We'll try to minimize some of the bad redraws
|
|
//
|
|
SendMessage(pfc->hwndView, WM_SETREDRAW, 0, 0L);
|
|
|
|
pfc->psv->lpVtbl->UIActivate(pfc->psv, SVUIA_DEACTIVATE);
|
|
pfc->psv->lpVtbl->DestroyViewWindow(pfc->psv);
|
|
pfc->hwndView = NULL;
|
|
}
|
|
|
|
if (pfc->lpndOpen)
|
|
{
|
|
OTRelease(pfc->lpndOpen);
|
|
pfc->lpndOpen = NULL;
|
|
}
|
|
|
|
if (pfc->pidl)
|
|
{
|
|
ILFree(pfc->pidl);
|
|
pfc->pidl=NULL;
|
|
}
|
|
|
|
Cabinet_RegisterDropTarget(pfc, FALSE);
|
|
|
|
pfc->psv->lpVtbl->Release(pfc->psv);
|
|
pfc->psv = NULL;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Maybe show the welcome screen...
|
|
TCHAR const g_szRegTips[] = REGSTR_PATH_EXPLORER TEXT("\\Tips");
|
|
TCHAR const g_szWelcomeShow[] = TEXT("Show");
|
|
|
|
UINT _RunWelcome(BOOL fInitShell)
|
|
{
|
|
HKEY hkey;
|
|
BOOL fShow=TRUE;
|
|
TCHAR szCmdLine[MAX_PATH];
|
|
STARTUPINFO startup;
|
|
PROCESS_INFORMATION pi;
|
|
UINT uPeek = PEEK_NORMAL;
|
|
|
|
startup.cb = SIZEOF(startup);
|
|
startup.lpReserved = NULL;
|
|
startup.lpDesktop = NULL;
|
|
startup.lpTitle = NULL;
|
|
startup.dwFlags = 0L;
|
|
startup.cbReserved2 = 0;
|
|
startup.lpReserved2 = NULL;
|
|
//startup.wShowWindow = wShowWindow;
|
|
|
|
if ((RegOpenKey(HKEY_CURRENT_USER, g_szRegTips, &hkey) == ERROR_SUCCESS))
|
|
{
|
|
DWORD cbData;
|
|
DWORD dwType;
|
|
|
|
cbData = SIZEOF(dwType);
|
|
if (RegQueryValueEx(hkey, (LPTSTR)g_szWelcomeShow, NULL, &dwType,
|
|
(LPBYTE)&fShow, &cbData) != ERROR_SUCCESS)
|
|
fShow = TRUE;
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
if (fShow)
|
|
{
|
|
lstrcpy(szCmdLine, TEXT("Welcome.exe"));
|
|
if (fInitShell)
|
|
lstrcat(szCmdLine, TEXT(" -f"));
|
|
|
|
if (CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL,
|
|
&startup, &pi))
|
|
{
|
|
if (fInitShell)
|
|
{
|
|
DWORD dwObject;
|
|
PFileCabinet pfc = GetPFC(v_hwndDesktop);
|
|
|
|
while (uPeek != PEEK_QUIT)
|
|
{
|
|
dwObject = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLINPUT);
|
|
// Are we done waiting?
|
|
if (dwObject == WAIT_OBJECT_0)
|
|
{
|
|
// Yep.
|
|
break;
|
|
}
|
|
else if (dwObject == WAIT_OBJECT_0 + 1)
|
|
{
|
|
// Nope, allow SendMessages to get through.
|
|
while ((uPeek = PeekForAMessage(pfc, v_hwndDesktop, TRUE))
|
|
== PEEK_CONTINUE)
|
|
{
|
|
; // Nothing to do here...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
}
|
|
|
|
return uPeek;
|
|
}
|
|
|
|
#ifdef WINNT
|
|
//---------------------------------------------------------------------------
|
|
// On NT, run the TASKMAN= line from the registry
|
|
TCHAR const g_szWinLogon[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
|
|
TCHAR const g_szTaskMan[] = TEXT("Taskman");
|
|
|
|
void _RunTaskMan( void )
|
|
{
|
|
HKEY hkeyWinLogon;
|
|
TCHAR szBuffer[MAX_PATH];
|
|
DWORD cbBuffer;
|
|
DWORD dwType;
|
|
STARTUPINFO startup;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, g_szWinLogon,
|
|
0, KEY_READ, &hkeyWinLogon) == ERROR_SUCCESS)
|
|
{
|
|
cbBuffer = SIZEOF(szBuffer);
|
|
if (RegQueryValueEx(hkeyWinLogon, g_szTaskMan, 0, &dwType,
|
|
(LPBYTE)szBuffer, &cbBuffer) == ERROR_SUCCESS)
|
|
{
|
|
if ( szBuffer[0] != TEXT('\0') )
|
|
{
|
|
startup.cb = SIZEOF(startup);
|
|
startup.lpReserved = NULL;
|
|
startup.lpDesktop = NULL;
|
|
startup.lpTitle = NULL;
|
|
startup.dwFlags = 0L;
|
|
startup.cbReserved2 = 0;
|
|
startup.lpReserved2 = NULL;
|
|
startup.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
if (CreateProcess(NULL, szBuffer, NULL, NULL, FALSE, 0,
|
|
NULL, NULL, &startup, &pi))
|
|
{
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hkeyWinLogon);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Handle a folder window being destroyed, free up any per-window storage.
|
|
//
|
|
|
|
LRESULT Cabinet_OnDestroy(HWND hwnd)
|
|
{
|
|
HICON hIcon;
|
|
HFONT hFont;
|
|
PFileCabinet pfc = GetPFC(hwnd);
|
|
|
|
//
|
|
// Destroy any icons that may be associated with the window
|
|
//
|
|
|
|
// destroy the large icon first for perf reasons..
|
|
// otherwise, user will immediately crunch the large icon down
|
|
// if we destroy the small one first
|
|
if (NULL != (hIcon = (HICON)SendMessage(hwnd, WM_SETICON, TRUE, 0L)))
|
|
{
|
|
if (hIcon != g_hIconDefOpenLarge)
|
|
DestroyIcon(hIcon);
|
|
}
|
|
|
|
if (NULL != (hIcon = (HICON)SendMessage(hwnd, WM_SETICON, FALSE, 0L)))
|
|
{
|
|
if (hIcon != g_hIconDefOpenSmall)
|
|
DestroyIcon(hIcon);
|
|
}
|
|
|
|
|
|
|
|
if (pfc)
|
|
{
|
|
// BUGBUG Do this properly
|
|
|
|
STDAPI SHFlushClipboard(void);
|
|
SHFlushClipboard();
|
|
|
|
if (pfc->hwndTreeTitle && (NULL != (hFont = (HFONT)SendMessage(pfc->hwndTreeTitle, WM_GETFONT, 0, 0L))))
|
|
{
|
|
DeleteObject(hFont);
|
|
}
|
|
|
|
OTUnregister(pfc->hwndMain);
|
|
FolderList_UnregisterWindow(pfc->hwndMain);
|
|
|
|
Cabinet_ReleaseShellView(pfc);
|
|
Assert(pfc->hwndView==NULL);
|
|
Assert(pfc->psv==NULL);
|
|
Assert(pfc->lpndOpen==NULL);
|
|
Assert(pfc->pidl==NULL);
|
|
|
|
// Clear out the window's menu so it will not be destroyed
|
|
// along with the window
|
|
SetMenu(pfc->hwndMain, NULL);
|
|
|
|
// Delete the current menu if it is not one of the global templates
|
|
// Note that menus are owned by processes, so when Cabinet goes
|
|
// away, the templates will be destroyed
|
|
if (pfc->hmenuCur && !Cabinet_IsTemplateMenu(pfc->hmenuCur))
|
|
{
|
|
DestroyMenu(pfc->hmenuCur);
|
|
pfc->hmenuCur = NULL;
|
|
}
|
|
|
|
SetWindowLong(pfc->hwndDrives, GWL_WNDPROC, (LONG)pfc->lpfnDrives);
|
|
DriveList_Reset(pfc);
|
|
|
|
if (pfc->pcmFind)
|
|
pfc->pcmFind->lpVtbl->Release(pfc->pcmFind);
|
|
|
|
CTreeDropTarget_Revoke(pfc);
|
|
|
|
if (pfc->hwndTree)
|
|
DestroyWindow(pfc->hwndTree);
|
|
|
|
if (pfc->lpndOpen)
|
|
OTRelease(pfc->lpndOpen);
|
|
|
|
ReleaseAndAssert((&pfc->sb)); // Must be 0
|
|
|
|
SetPFC(hwnd, NULL);
|
|
|
|
}
|
|
|
|
DebugMsg(DM_TRACE, TEXT("c.c_od: Posting quit message for %#08x"), GetCurrentThreadId());
|
|
PostQuitMessage(0); // quit this thread.
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Closing a cabinet window.
|
|
//
|
|
// save it's local view info in the directory it is looking at
|
|
//
|
|
// NOTE: this will fail on read only media like net or cdrom
|
|
//
|
|
// REVIEW: we may not want to save this info on removable media
|
|
// (but if we don't allow a switch to force this!)
|
|
//
|
|
|
|
BOOL Cabinet_SaveState(HWND hwnd, WINDOWPLACEMENT * lpwp,
|
|
BOOL fAlwaysSave, BOOL fAddToRestart, BOOL fDestroyWindow)
|
|
{
|
|
FOLDERSETTINGS fs;
|
|
WINDOWPLACEMENT wp;
|
|
LPSTREAM pstm;
|
|
CABSH cabsh;
|
|
BOOL fRestricted;
|
|
PFileCabinet pfc = GetPFC(hwnd);
|
|
|
|
// Don't save any state info if restrictions are in place.
|
|
fRestricted = SHRestricted(REST_NOSAVESET);
|
|
|
|
if (pfc && pfc->psv)
|
|
{
|
|
cabsh.wv = pfc->wv;
|
|
|
|
// See if we are actually supposed to save anything
|
|
|
|
// Now get the view information
|
|
pfc->psv->lpVtbl->GetCurrentInfo(pfc->psv, &fs);
|
|
|
|
if (!fRestricted && (fAlwaysSave || g_CabState.fSaveLocalView))
|
|
{
|
|
// First try to get a stream for the data.
|
|
if (pfc->hwndTree)
|
|
{
|
|
// We have a custom built home for the explorer view settings.
|
|
// NB Last writer wins.
|
|
pstm = OpenRegStream(g_hkeyExplorer, c_szCabinetExpView, c_szSettings, STGM_WRITE);
|
|
}
|
|
else {
|
|
if (!g_CabState.fNewWindowMode && !(hwnd == v_hwndTray || hwnd == v_hwndDesktop) && pfc->fSBWSaved)
|
|
{
|
|
// We're in OneWinView and we've already saved state.
|
|
pstm = NULL;
|
|
}
|
|
else
|
|
{
|
|
// When in OneWinView mode, the first save will set the default
|
|
// for this pidl. We don't want to muck pidl saves after that, so:
|
|
pfc->fSBWSaved = TRUE;
|
|
|
|
pstm = Cabinet_GetViewStreamForPidl(pfc->pidl, STGM_WRITE, c_szCabStreamInfo);
|
|
}
|
|
|
|
// if these keys are down, save the current states
|
|
if (hwnd != v_hwndTray && hwnd != v_hwndDesktop &&
|
|
GetAsyncKeyState(VK_CONTROL) < 0) {
|
|
|
|
if (g_CabState.fNewWindowMode) {
|
|
g_dfs.bDefToolBarMulti = pfc->wv.bToolBar;
|
|
} else {
|
|
g_dfs.bDefToolBarSingle = pfc->wv.bToolBar;
|
|
}
|
|
|
|
g_dfs.uDefViewMode = fs.ViewMode;
|
|
g_dfs.bDefStatusBar = pfc->wv.bStatusBar;
|
|
|
|
SaveDefaultFolderSettings();
|
|
}
|
|
|
|
}
|
|
if (pstm)
|
|
{
|
|
ULARGE_INTEGER uliOffset = {0, 0};
|
|
LARGE_INTEGER li = {0, 0};
|
|
|
|
|
|
// The window position comes from the folder, everything else comes
|
|
// from the view.
|
|
if (lpwp)
|
|
{
|
|
// The tray needs to pass in spefic data.
|
|
wp = *lpwp;
|
|
wp.length = SIZEOF(WINDOWPLACEMENT);
|
|
}
|
|
else
|
|
{
|
|
wp.length = SIZEOF(WINDOWPLACEMENT);
|
|
GetWindowPlacement(hwnd, &wp);
|
|
}
|
|
cabsh.dwHotkey = (UINT)SendMessage(hwnd, WM_GETHOTKEY, 0, 0);
|
|
|
|
//
|
|
// Now Lets convert all of this common stuff into a
|
|
// non 16/32 bit dependant data structure, such that both
|
|
// can us it.
|
|
//
|
|
cabsh.dwSize = SIZEOF(cabsh);
|
|
cabsh.flags = wp.flags;
|
|
cabsh.showCmd = wp.showCmd;
|
|
cabsh.ptMinPosition.x = wp.ptMinPosition.x;
|
|
cabsh.ptMinPosition.y = wp.ptMinPosition.y;
|
|
cabsh.ptMaxPosition.x = wp.ptMaxPosition.x;
|
|
cabsh.ptMaxPosition.y = wp.ptMaxPosition.y;
|
|
|
|
cabsh.rcNormalPosition.left = wp.rcNormalPosition.left;
|
|
cabsh.rcNormalPosition.right = wp.rcNormalPosition.right;
|
|
cabsh.rcNormalPosition.top = wp.rcNormalPosition.top;
|
|
cabsh.rcNormalPosition.bottom = wp.rcNormalPosition.bottom;
|
|
|
|
// Now the folder settings
|
|
cabsh.ViewMode = fs.ViewMode;
|
|
// NB Don't ever preserve the best-fit flag or the nosubfolders flag.
|
|
cabsh.fFlags = fs.fFlags & ~FWF_NOSUBFOLDERS & ~FWF_BESTFITWINDOW;
|
|
|
|
cabsh.TreeSplit = pfc->TreeSplit;
|
|
|
|
//
|
|
// First output the common non view specific information
|
|
//
|
|
pstm->lpVtbl->Write(pstm, &cabsh, SIZEOF(cabsh), NULL);
|
|
|
|
// And release it, which will commit it to disk..
|
|
pstm->lpVtbl->Release(pstm);
|
|
|
|
// Last but not least save away the view state.
|
|
pfc->psv->lpVtbl->SaveViewState(pfc->psv);
|
|
}
|
|
}
|
|
|
|
// BUGBUG: we should move this right before the GetViewStream
|
|
// because both of these hit cabinet.ini while pstm->lpVtbl->Release
|
|
// hits desktop.ini
|
|
//
|
|
// If the command was to close, simply destroy the window, else
|
|
// add the file to the list of files to be restarted.
|
|
//
|
|
if (!fRestricted && fAddToRestart)
|
|
{
|
|
return AddCabinetToRestartList(pfc->hwndTree?
|
|
COF_CREATENEWWINDOW | COF_EXPLORE : COF_CREATENEWWINDOW,
|
|
pfc->pidl);
|
|
}
|
|
}
|
|
|
|
if (fDestroyWindow)
|
|
return DestroyWindow(hwnd);
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Return the HWND of the folder with the given path.
|
|
|
|
HWND Cabinet_FindByPidl(LPCITEMIDLIST pidl)
|
|
{
|
|
HWND hwnd;
|
|
|
|
// walk all windows of our class
|
|
for (hwnd = FindWindow(c_szCabinetClass, NULL); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
|
|
{
|
|
if (Cabinet_IsFolderWindow(hwnd)
|
|
&& Desktop_IsSameRoot(hwnd,pidl))
|
|
return hwnd;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// returns:
|
|
// TRUE User wants a new cabinet window.
|
|
// FALSE User wants re-use an existing cabinet.
|
|
BOOL Settings_IsNewCabinetRequired(HWND hwnd)
|
|
{
|
|
HWND hwndTree;
|
|
BOOL ret = TRUE; // Default to new window if nobody wants to make
|
|
// a decision below
|
|
BOOL fNewWindowMode = g_CabState.fNewWindowMode;
|
|
|
|
//
|
|
// JoeB. Control key reverses the fNewWindowMode flag.
|
|
//
|
|
if (GetKeyState(VK_CONTROL) < 0) {
|
|
fNewWindowMode = !fNewWindowMode;
|
|
}
|
|
|
|
if (fNewWindowMode) {
|
|
// Use the default.
|
|
// REVIEW, default is to depend on the split.
|
|
|
|
// if the hwnd passed in is NULL, we have no window
|
|
// to replace.
|
|
if (hwnd) {
|
|
if (NULL != (hwndTree = GetDlgItem(hwnd, FCIDM_TREE)))
|
|
ret = !Cabinet_IsVisible(hwndTree);
|
|
}
|
|
} else {
|
|
ret = FALSE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Create a window for the given folder restoring it's
|
|
// settings along the way.
|
|
// Returns hwnd of newly created window, NULL if something goes
|
|
// wrong.
|
|
HRESULT CreateWindowForFolder(LPCITEMIDLIST pidl, UINT uFlags, int nCmdShow, HWND *phwnd)
|
|
{
|
|
HRESULT hres;
|
|
CABVIEW cv;
|
|
|
|
Cabinet_GetStateFromPidl(pidl, &cv, uFlags);
|
|
|
|
// Explore if and only if that's what the user said to do
|
|
if (uFlags & COF_EXPLORE)
|
|
{
|
|
cv.wv.bTree = TRUE;
|
|
}
|
|
else
|
|
{
|
|
cv.wv.bTree = FALSE;
|
|
}
|
|
|
|
|
|
// If the show command doesn't specifiy anything special use
|
|
// the last saved show command except when it's SHOWMINIMIZE.
|
|
// NB If the WP looks invalid then ignore it.
|
|
// However if SHOWDEFAULT is passed in then allow us to restore
|
|
// minimized.
|
|
if (nCmdShow == SW_SHOWDEFAULT)
|
|
{
|
|
if (cv.wp.length)
|
|
nCmdShow = cv.wp.showCmd;
|
|
else
|
|
nCmdShow = SW_SHOWNORMAL;
|
|
|
|
}
|
|
else if (nCmdShow == SW_SHOWNORMAL && cv.wp.length)
|
|
{
|
|
if (cv.wp.showCmd == SW_SHOWMINIMIZED)
|
|
nCmdShow = SW_SHOWNORMAL;
|
|
else
|
|
nCmdShow = cv.wp.showCmd;
|
|
}
|
|
|
|
hres = Cabinet_CreateWindow(&cv, pidl, nCmdShow, phwnd);
|
|
|
|
if (hres==NOERROR)
|
|
{
|
|
if (cv.wHotkey)
|
|
Window_SetHotkey(*phwnd, cv.wHotkey);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
//---------------------------------------------------------------------------
|
|
// Copy the exception info so we can get debug info for Raised exceptions
|
|
// which don't go through the debugger.
|
|
void _CopyExceptionInfo(LPEXCEPTION_POINTERS pep)
|
|
{
|
|
PEXCEPTION_RECORD per;
|
|
|
|
per = pep->ExceptionRecord;
|
|
DebugMsg(DM_ERROR, TEXT("Exception %x at %#08x."), per->ExceptionCode, per->ExceptionAddress);
|
|
|
|
if (per->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
|
|
{
|
|
// If the first param is 1 then this was a write.
|
|
// If the first param is 0 then this was a read.
|
|
if (per->ExceptionInformation[0])
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("Invalid write to %#08x."), per->ExceptionInformation[1]);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("Invalid read of %#08x."), per->ExceptionInformation[1]);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
#define _CopyExceptionInfo(x) TRUE
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
HNFBLOCK ConvertNFItoHNFBLOCK(PNEWFOLDERINFO pInfo, DWORD dwProcId)
|
|
{
|
|
UINT uSize;
|
|
UINT uSzPath;
|
|
UINT uSzRoot;
|
|
UINT uPidl;
|
|
UINT uPidlSelect;
|
|
UINT uPidlRoot;
|
|
PNEWFOLDERBLOCK pnfb;
|
|
LPBYTE lpb;
|
|
HNFBLOCK hBlock;
|
|
|
|
uSize = SIZEOF(NEWFOLDERBLOCK);
|
|
if (pInfo->pszPath)
|
|
{
|
|
uSzPath = (lstrlen(pInfo->pszPath) + 1) * SIZEOF(TCHAR);
|
|
uSize += uSzPath;
|
|
}
|
|
if (pInfo->pszRoot)
|
|
{
|
|
uSzRoot = (lstrlen(pInfo->pszRoot) + 1) * SIZEOF(TCHAR);
|
|
uSize += uSzRoot;
|
|
}
|
|
if (pInfo->pidl)
|
|
{
|
|
uPidl = ILGetSize(pInfo->pidl);
|
|
uSize += uPidl;
|
|
}
|
|
if (pInfo->pidlSelect)
|
|
{
|
|
uPidlSelect = ILGetSize(pInfo->pidlSelect);
|
|
uSize += uPidlSelect;
|
|
}
|
|
if (pInfo->pidlRoot)
|
|
{
|
|
uPidlRoot = ILGetSize(pInfo->pidlRoot);
|
|
uSize += uPidlRoot;
|
|
}
|
|
|
|
hBlock = SHAllocShared(NULL, uSize, dwProcId);
|
|
if (hBlock == NULL)
|
|
return NULL;
|
|
|
|
pnfb = SHLockShared(hBlock, dwProcId);
|
|
if (pnfb == NULL)
|
|
{
|
|
SHFreeShared(hBlock, dwProcId);
|
|
return NULL;
|
|
}
|
|
|
|
pnfb->dwSize = uSize;
|
|
pnfb->uFlags = pInfo->uFlags;
|
|
pnfb->nShow = pInfo->nShow;
|
|
pnfb->hwndCaller = pInfo->hwndCaller;
|
|
pnfb->dwHotKey = pInfo->dwHotKey;
|
|
pnfb->clsid = pInfo->clsid;
|
|
pnfb->clsidInProc = pInfo->clsidInProc;
|
|
pnfb->oszPath = 0;
|
|
pnfb->oszRoot = 0;
|
|
pnfb->oidl = 0;
|
|
pnfb->oidlSelect = 0;
|
|
pnfb->oidlRoot = 0;
|
|
|
|
lpb = (LPBYTE)(pnfb+1); // Point just past the structure
|
|
|
|
if (pInfo->pszPath)
|
|
{
|
|
memcpy(lpb,pInfo->pszPath,uSzPath);
|
|
pnfb->oszPath = (lpb-(LPBYTE)pnfb);
|
|
lpb += uSzPath;
|
|
}
|
|
if (pInfo->pszRoot)
|
|
{
|
|
memcpy(lpb,pInfo->pszRoot,uSzRoot);
|
|
pnfb->oszRoot = (lpb-(LPBYTE)pnfb);
|
|
lpb += uSzRoot;
|
|
}
|
|
if (pInfo->pidl)
|
|
{
|
|
memcpy(lpb,pInfo->pidl,uPidl);
|
|
pnfb->oidl = (lpb-(LPBYTE)pnfb);
|
|
lpb += uPidl;
|
|
}
|
|
if (pInfo->pidlSelect)
|
|
{
|
|
memcpy(lpb,pInfo->pidlSelect,uPidlSelect);
|
|
pnfb->oidlSelect = (lpb-(LPBYTE)pnfb);
|
|
lpb += uPidlSelect;
|
|
}
|
|
if (pInfo->pidlRoot)
|
|
{
|
|
memcpy(lpb,pInfo->pidlRoot,uPidlRoot);
|
|
pnfb->oidlRoot = (lpb-(LPBYTE)pnfb);
|
|
lpb += uPidlRoot;
|
|
}
|
|
SHUnlockShared(pnfb);
|
|
return hBlock;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
PNEWFOLDERINFO ConvertHNFBLOCKtoNFI(HNFBLOCK hBlock, DWORD dwProcId)
|
|
{
|
|
PNEWFOLDERBLOCK pnfb;
|
|
PNEWFOLDERINFO pInfo;
|
|
|
|
pnfb = SHLockShared(hBlock,dwProcId);
|
|
if (pnfb == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (pnfb->dwSize < SIZEOF(NEWFOLDERBLOCK))
|
|
{
|
|
SHUnlockShared(pnfb);
|
|
return NULL;
|
|
}
|
|
|
|
pInfo = Alloc(SIZEOF(NEWFOLDERINFO));
|
|
if (pInfo == NULL)
|
|
{
|
|
SHUnlockShared(pnfb);
|
|
return NULL;
|
|
}
|
|
|
|
pInfo->pszPath = NULL;
|
|
pInfo->pidl = NULL;
|
|
pInfo->uFlags = pnfb->uFlags;
|
|
pInfo->nShow = pnfb->nShow;
|
|
pInfo->hwndCaller = pnfb->hwndCaller;
|
|
pInfo->dwHotKey = pnfb->dwHotKey;
|
|
pInfo->clsid = pnfb->clsid;
|
|
pInfo->clsidInProc = pnfb->clsidInProc;
|
|
|
|
if (pnfb->oszPath)
|
|
Str_SetPtr(&pInfo->pszPath,(LPTSTR)((LPBYTE)pnfb+pnfb->oszPath));
|
|
|
|
if (pnfb->oszRoot)
|
|
Str_SetPtr(&pInfo->pszRoot,(LPTSTR)((LPBYTE)pnfb+pnfb->oszRoot));
|
|
|
|
if (pnfb->oidl)
|
|
pInfo->pidl = ILGlobalClone((LPITEMIDLIST)((LPBYTE)pnfb+pnfb->oidl));
|
|
|
|
if (pnfb->oidlSelect)
|
|
pInfo->pidlSelect = ILGlobalClone((LPITEMIDLIST)((LPBYTE)pnfb+pnfb->oidlSelect));
|
|
|
|
if (pnfb->oidlRoot)
|
|
pInfo->pidlRoot = ILGlobalClone((LPITEMIDLIST)((LPBYTE)pnfb+pnfb->oidlRoot));
|
|
|
|
SHUnlockShared(pnfb);
|
|
SHFreeShared(hBlock,dwProcId);
|
|
|
|
return pInfo;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
DWORD WINAPI _ThreadInit(PNEWFOLDERINFO pFolderInfo)
|
|
{
|
|
IUnknown_AddRef(&g_unkRef);
|
|
|
|
// Create a cabinet... don't let bad cabinets toast the whole shell
|
|
_try
|
|
{
|
|
HWND hwnd = NULL;
|
|
HRESULT hres;
|
|
PSFCACHE psfc = SFCInitializeThread();
|
|
if (psfc)
|
|
{
|
|
hres = CreateWindowForFolder(pFolderInfo->pidl, pFolderInfo->uFlags, pFolderInfo->nShow, &hwnd);
|
|
|
|
if (pFolderInfo->dwHotKey && hwnd) {
|
|
DebugMsg(DM_TRACE, TEXT("Cabinet: setting hotkey to be %d"), pFolderInfo->dwHotKey);
|
|
Window_SetHotkey(hwnd, pFolderInfo->dwHotKey);
|
|
}
|
|
|
|
if (pFolderInfo->hwndCaller)
|
|
SetAppStartingCursor(pFolderInfo->hwndCaller, FALSE);
|
|
|
|
OFLRemove(pFolderInfo->pidl);
|
|
|
|
if (hwnd)
|
|
{
|
|
// Let interested people know we are alive
|
|
//
|
|
if (pFolderInfo->uFlags&COF_SELECT)
|
|
{
|
|
FileCabinet_SelectItem(hwnd, SVSI_SELFLAGS,
|
|
pFolderInfo->pidlSelect);
|
|
}
|
|
|
|
SignalFileOpen(pFolderInfo->pidl);
|
|
|
|
MessageLoop(hwnd);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("SH TR _ThreadInit CreateWinfowForFolder Fail hres=%x"), hres);
|
|
|
|
if (hres==ResultFromScode(E_OUTOFMEMORY))
|
|
{
|
|
MSG msg;
|
|
TCHAR szTitle[80];
|
|
LPTSTR pszTitle = NULL;
|
|
|
|
// Try to abort any others from being started!.
|
|
SHAbortInvokeCommand();
|
|
|
|
// Remove all QM_QUIT messages from the queue to make MessageBox happy
|
|
while(PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE))
|
|
{ };
|
|
|
|
if (LoadString(hinstCabinet, IDS_FILECABINET, szTitle, ARRAYSIZE(szTitle))) {
|
|
pszTitle = szTitle;
|
|
}
|
|
|
|
SHOutOfMemoryMessageBox(pFolderInfo->hwndCaller, pszTitle,
|
|
MB_OK|MB_ICONHAND);
|
|
}
|
|
}
|
|
SFCTerminateThread();
|
|
}
|
|
else
|
|
OFLRemove(pFolderInfo->pidl);
|
|
}
|
|
// Pass exception to the debugger if there is one, otherwise handle it here.
|
|
_except(_CopyExceptionInfo(GetExceptionInformation()),
|
|
UnhandledExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
// BUGBUG:: We should try to do some more cleanup here, like
|
|
// making sure we don't own critical sections and the like.
|
|
// I would have done this but there are no apis to do this...
|
|
|
|
// Drop the cursor count just in case a folder put up the wait cursor
|
|
// before faulting.
|
|
SetAppStartingCursor(v_hwndDesktop, FALSE);
|
|
|
|
// Also we will try to display a message box to tell the user
|
|
// that a thread has died...
|
|
//
|
|
ShellMessageBox(hinstCabinet, NULL, MAKEINTRESOURCE(IDS_EXCEPTIONMSG),
|
|
MAKEINTRESOURCE(IDS_CABINET), MB_ICONEXCLAMATION|MB_SETFOREGROUND);
|
|
}
|
|
|
|
// Terminate this thread.
|
|
ILFree(pFolderInfo->pidl);
|
|
if (pFolderInfo->uFlags & COF_SELECT)
|
|
{
|
|
ILFree(pFolderInfo->pidlSelect);
|
|
}
|
|
GlobalFree((HGLOBAL)pFolderInfo);
|
|
|
|
IUnknown_Release(&g_unkRef);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const TCHAR c_szIDListSwitch[] = TEXT("/IDList");
|
|
const TCHAR c_szInProcSwitch[] = TEXT("/InProc");
|
|
const TCHAR c_szNoUISwitch[] = TEXT("/NoUI");
|
|
|
|
BOOL Parse_FileName(LPCTSTR lpszCmdLine, LPTSTR szField, UINT cbField,
|
|
int *pi, LPITEMIDLIST *ppidl, LPTSTR *ppsz)
|
|
{
|
|
if (lstrcmpi(szField, c_szIDListSwitch) == 0)
|
|
{
|
|
LPITEMIDLIST pidlGlobal = NULL;
|
|
HANDLE hMem;
|
|
DWORD dwProcId;
|
|
LPTSTR pszNextColon;
|
|
|
|
// Parse and skip the next arg
|
|
if (!ParseField(lpszCmdLine, ++*pi, szField, cbField))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
// Convert the string into a pointer.
|
|
// IDLists start with ':', so add 1
|
|
hMem = (HANDLE)StrToLong(szField+1);
|
|
pszNextColon = StrChr(szField+1,TEXT(':'));
|
|
if (pszNextColon)
|
|
{
|
|
dwProcId = (DWORD)StrToLong(pszNextColon+1);
|
|
pidlGlobal = SHLockShared(hMem,dwProcId);
|
|
}
|
|
|
|
if (pidlGlobal)
|
|
{
|
|
if (!IsBadReadPtr(pidlGlobal, 1))
|
|
{
|
|
*ppidl = ILGlobalClone(pidlGlobal);
|
|
|
|
// String or IDList, not both
|
|
Str_SetPtr(ppsz, NULL);
|
|
}
|
|
SHUnlockShared(pidlGlobal);
|
|
SHFreeShared(hMem,dwProcId);
|
|
}
|
|
}
|
|
else if (*szField) // ignore if it is an empty string
|
|
{
|
|
Str_SetPtr(ppsz, szField);
|
|
|
|
// String or IDList, not both
|
|
if (*ppidl)
|
|
{
|
|
ILGlobalFree(*ppidl);
|
|
*ppidl = NULL; // do not free twice...
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Check the registry for a shell root under this CLSID.
|
|
BOOL GetRootFromRootClass(LPCTSTR pszGUID, LPTSTR pszPath, int cchPath)
|
|
{
|
|
HKEY hkeyNew;
|
|
BOOL fRet = FALSE;
|
|
DWORD dwType;
|
|
TCHAR szClass[MAX_PATH];
|
|
DWORD cbPath = cchPath * sizeof(TCHAR);
|
|
VDATEINPUTBUF(pszPath, TCHAR, cchPath);
|
|
|
|
wsprintf(szClass, TEXT("CLSID\\%s\\ShellExplorerRoot"), pszGUID);
|
|
|
|
if (!GetSystemMetrics(SM_CLEANBOOT) && (RegOpenKey(HKEY_CLASSES_ROOT, szClass, &hkeyNew) == ERROR_SUCCESS))
|
|
{
|
|
if (RegQueryValueEx(hkeyNew, NULL, 0, &dwType, (LPBYTE)pszPath, &cbPath) == ERROR_SUCCESS)
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
RegCloseKey(hkeyNew);
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
BOOL Parse_CmdLine(LPCTSTR lpszCmdLine, PNEWFOLDERINFO pfi)
|
|
{
|
|
UINT *puFlags = &pfi->uFlags;
|
|
CLSID *pclsid = &pfi->clsid;
|
|
int i;
|
|
TCHAR szField[MAX_PATH];
|
|
|
|
pfi->pidl = NULL;
|
|
pfi->pszPath = NULL;
|
|
pfi->pidlRoot = NULL;
|
|
pfi->pszRoot = NULL;
|
|
|
|
*puFlags = COF_NORMAL;
|
|
|
|
// Empty command line implies "/n,/e,N:\", where N:\ is windows root.
|
|
if (*lpszCmdLine == TEXT('\0'))
|
|
{
|
|
*puFlags |= COF_CREATENEWWINDOW | COF_EXPLORE;
|
|
GetWindowsDirectory(szField, ARRAYSIZE(szField));
|
|
|
|
while(*szField && !PathIsRoot(szField))
|
|
{
|
|
PathRemoveFileSpec(szField);
|
|
}
|
|
|
|
if (*szField) {
|
|
Str_SetPtr(&pfi->pszPath, szField);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Arguments must be separated by '=' or ','
|
|
for (i=1; ParseField(lpszCmdLine, i, szField, ARRAYSIZE(szField)); ++i)
|
|
{
|
|
if (lstrcmpi(szField, c_szForceNewWindowSwitch) == 0) {
|
|
*puFlags |= COF_CREATENEWWINDOW;
|
|
} else if (lstrcmpi(szField, c_szUseOpenSettingsSwitch) == 0) {
|
|
*puFlags |= COF_USEOPENSETTINGS;
|
|
} else if (lstrcmpi(szField, c_szExploreSwitch) == 0) {
|
|
*puFlags |= COF_EXPLORE;
|
|
} else if (lstrcmpi(szField, c_szNewRootSwitch) == 0) {
|
|
// Parse and skip the next arg or 2
|
|
if (!ParseField(lpszCmdLine, ++i, szField, ARRAYSIZE(szField)))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
if (SUCCEEDED(SHCLSIDFromString(szField, pclsid)))
|
|
{
|
|
TCHAR szGUID[MAX_PATH];
|
|
|
|
lstrcpy(szGUID, szField);
|
|
// This arg was a GUID specifying the class.
|
|
// There should be a root path on the command line.
|
|
if (!ParseField(lpszCmdLine, ++i, szField,
|
|
ARRAYSIZE(szField)))
|
|
{
|
|
// Nope, see if there's one in the reg.
|
|
if (!GetRootFromRootClass(szGUID, szField, ARRAYSIZE(szField)))
|
|
{
|
|
// Nope, CL is bogus.
|
|
return(FALSE);
|
|
}
|
|
|
|
// Else, fall through and parse it.
|
|
|
|
}
|
|
*puFlags |= COF_ROOTCLASS;
|
|
}
|
|
|
|
if (Parse_FileName(lpszCmdLine, szField,
|
|
ARRAYSIZE(szField), &i, &pfi->pidlRoot,
|
|
&pfi->pszRoot))
|
|
{
|
|
*puFlags |= COF_NEWROOT;
|
|
}
|
|
else
|
|
{
|
|
// Can't have a root class without a root
|
|
*puFlags &= ~COF_ROOTCLASS;
|
|
return(FALSE);
|
|
}
|
|
} else if (lstrcmpi(szField, c_szInProcSwitch) == 0) {
|
|
// Parse and skip the next arg or 2
|
|
if (!ParseField(lpszCmdLine, ++i, szField, ARRAYSIZE(szField)))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
// The next arg must be a GUID
|
|
if (FAILED(SHCLSIDFromString(szField, &pfi->clsidInProc)))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
*puFlags |= COF_INPROC;
|
|
} else if (lstrcmpi(szField, c_szSelectSwitch) == 0) {
|
|
*puFlags |= COF_SELECT;
|
|
} else if (lstrcmpi(szField, c_szNoUISwitch) == 0) {
|
|
*puFlags |= COF_NOUI;
|
|
} else {
|
|
Parse_FileName(lpszCmdLine, szField,
|
|
ARRAYSIZE(szField), &i, &pfi->pidl,
|
|
&pfi->pszPath);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void Cabinet_CleanUpCommand(PNEWFOLDERINFO pfi)
|
|
{
|
|
Str_SetPtr(&pfi->pszPath, NULL);
|
|
if (pfi->pidl)
|
|
{
|
|
ILGlobalFree(pfi->pidl);
|
|
}
|
|
|
|
Str_SetPtr(&pfi->pszRoot, NULL);
|
|
if (pfi->pidlRoot)
|
|
{
|
|
ILGlobalFree(pfi->pidlRoot);
|
|
}
|
|
|
|
Free(pfi);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Attempts to activate an already existing folder window taking note of user
|
|
// preferences.
|
|
// Returns TRUE if an old instance was found and activated.
|
|
// Returns FALSE otherwise.
|
|
BOOL ActivateOrResetOtherInstance(LPCITEMIDLIST pidl, PNEWFOLDERINFO pInfo)
|
|
{
|
|
HWND hwnd;
|
|
UINT uFlags = pInfo->uFlags;
|
|
|
|
if (uFlags & COF_EXPLORE)
|
|
{
|
|
// Never in place if EXPLORE is chosen
|
|
return(FALSE);
|
|
}
|
|
|
|
// Quick check to see of the folder already exists.
|
|
// We optionally may wait if there are pending waits
|
|
if (uFlags & COF_WAITFORPENDING)
|
|
SHWaitForFileToOpen((LPITEMIDLIST)pidl, WFFO_WAIT | WFFO_REMOVE, WFFO_WAITTIME);
|
|
|
|
// Always open in place if possible
|
|
if (uFlags & COF_USEOPENSETTINGS)
|
|
{
|
|
// We always want to
|
|
// Folder window doesn't already exist.
|
|
// Change view in place or open a new window?
|
|
hwnd = GetForegroundWindow();
|
|
if (hwnd && IsWindowVisible(hwnd)
|
|
&& Cabinet_IsFolderWindow(hwnd) && !Settings_IsNewCabinetRequired(hwnd))
|
|
{
|
|
BOOL fSetPathSucceeded;
|
|
HANDLE hPath;
|
|
DWORD dwProcId;
|
|
|
|
GetWindowThreadProcessId(hwnd, &dwProcId);
|
|
hPath = SHAllocShared(pidl, ILGetSize(pidl), dwProcId);
|
|
|
|
if (hPath)
|
|
{
|
|
// maybe across processes so use SendMessage
|
|
// REVIEW: pidl is not shared among processes.
|
|
if (uFlags&COF_SELECT)
|
|
{
|
|
fSetPathSucceeded = SendMessage(hwnd, CWM_SETPATH, 0, (LPARAM)hPath);
|
|
if (fSetPathSucceeded)
|
|
{
|
|
FileCabinet_SelectItem(hwnd, SVSI_SELFLAGS,
|
|
pInfo->pidlSelect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No need to wait for the window to update if we don't
|
|
// have anything to select
|
|
fSetPathSucceeded = SendMessage(hwnd, CWM_SETPATH, CSP_REPOST, (LPARAM)hPath);
|
|
}
|
|
DebugMsg(DM_TRACE, TEXT("ca TR - AOR CWM_SETPATH returned (%d)"), fSetPathSucceeded);
|
|
}
|
|
else
|
|
{
|
|
fSetPathSucceeded = FALSE;
|
|
}
|
|
|
|
if (fSetPathSucceeded)
|
|
{
|
|
// we're in the USEOPENSETTINGS, which means we don't munge with
|
|
// with the previously opened window unless it's iconic
|
|
|
|
// if this screws AfterDark, tough noogies... they shouldn't
|
|
// be coming through this code path anyways.
|
|
// they should be using COF_NORMAL and not COF_USEOPENSETTINGS
|
|
if (IsIconic(hwnd))
|
|
ShowWindow(hwnd, pInfo->nShow);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
hwnd = Cabinet_FindByPidl(pidl);
|
|
if (hwnd && IsWindowVisible(hwnd))
|
|
{
|
|
// Yep, don't bother creating a new one.
|
|
ShowWindow(hwnd, pInfo->nShow);
|
|
|
|
// if (IsIconic(hwnd))
|
|
// ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
|
|
SetForegroundWindow(hwnd);
|
|
|
|
if (uFlags&COF_SELECT)
|
|
{
|
|
FileCabinet_SelectItem(hwnd, SVSI_SELFLAGS,
|
|
pInfo->pidlSelect);
|
|
}
|
|
|
|
// Let anyone who is wait for this window to open know that
|
|
// it is open
|
|
SignalFileOpen((LPITEMIDLIST)pidl);
|
|
|
|
Window_SetHotkey(hwnd, pInfo->dwHotKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Failed to activate an existing folder window.
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// BUGBUG: Stolen from ShellExecuteEx, but note that we add a conncetion
|
|
// and never delete it.
|
|
int _ValidateUNC(HWND hwndOwner, LPTSTR pszFile)
|
|
{
|
|
if (!SHValidateUNC(hwndOwner, pszFile, FALSE))
|
|
{
|
|
return(GetLastError());
|
|
}
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
BOOL Cabinet_OpenFolderPath(PNEWFOLDERINFO pInfo)
|
|
{
|
|
HWND hwnd = pInfo->hwndCaller;
|
|
LPCTSTR pcszPath = pInfo->pszPath;
|
|
UINT uFlags = pInfo->uFlags;
|
|
int nCmdShow = pInfo->nShow;
|
|
|
|
LPITEMIDLIST pidl;
|
|
BOOL fSuccess = FALSE;
|
|
DWORD gfInOut;
|
|
TCHAR szFolderPath[MAX_PATH];
|
|
LPTSTR pszPath;
|
|
|
|
if (uFlags & COF_NOUI)
|
|
{
|
|
g_fRunNoUI = TRUE;
|
|
IUnknown_AddRef(&g_unkRef); // Get us into the 0 ref state
|
|
IUnknown_Release(&g_unkRef); // (for appropriate timeout)
|
|
return TRUE;
|
|
}
|
|
|
|
if (uFlags & COF_INPROC)
|
|
{
|
|
// We are going to ignore all the other switches if we have /inproc
|
|
IUnknown *punk;
|
|
|
|
if (SUCCEEDED(SHCoCreateInstance(NULL, &pInfo->clsidInProc, NULL,
|
|
&IID_IUnknown, &punk)))
|
|
{
|
|
IPersistFile *ppf;
|
|
|
|
if (pInfo->pszPath &&
|
|
SUCCEEDED(punk->lpVtbl->QueryInterface(punk, &IID_IPersistFile,
|
|
&ppf)))
|
|
{
|
|
WCHAR wszPath[MAX_PATH];
|
|
|
|
// copy the link instead of linking to it
|
|
StrToOleStrN(wszPath, ARRAYSIZE(wszPath), pInfo->pszPath, -1);
|
|
ppf->lpVtbl->Load(ppf, wszPath, 0);
|
|
|
|
IUnknown_Release(ppf);
|
|
}
|
|
IUnknown_Release(punk);
|
|
}
|
|
|
|
// HACK In case the inproc guy didn't AddRef on us, we will AddRef and
|
|
// Release because the message to die is only posted when the Release
|
|
// results in the refcount going to 0
|
|
IUnknown_AddRef(&g_unkRef);
|
|
IUnknown_Release(&g_unkRef);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
if (pInfo->pidl)
|
|
{
|
|
// Apparently we already have an IDList
|
|
return(Cabinet_OpenFolder(pInfo));
|
|
}
|
|
|
|
// We need a copy because ValidateUNC could make a net connection and
|
|
// change the path to a drive letter for us.
|
|
if (pcszPath)
|
|
{
|
|
lstrcpyn(szFolderPath, pcszPath, ARRAYSIZE(szFolderPath));
|
|
}
|
|
else
|
|
{
|
|
szFolderPath[0] = TEXT('\0');
|
|
}
|
|
pszPath = szFolderPath;
|
|
|
|
if (!(uFlags & COF_NEWROOT))
|
|
{
|
|
// Don't check for UNC names if non-standard root
|
|
if (PathIsUNC(pszPath))
|
|
{
|
|
switch (_ValidateUNC(hwnd, pszPath))
|
|
{
|
|
case WN_CANCEL:
|
|
return(FALSE);
|
|
|
|
case WN_SUCCESS:
|
|
break;
|
|
|
|
default:
|
|
goto ShowError;
|
|
}
|
|
}
|
|
}
|
|
|
|
gfInOut = SFGAO_FOLDER;
|
|
switch (OTILCreateFromPath(pszPath, &pidl, &gfInOut))
|
|
{
|
|
case NOERROR:
|
|
break;
|
|
|
|
case ResultFromScode(E_OUTOFMEMORY):
|
|
// BUGBUG: Show a message
|
|
return(FALSE);
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_CANCELLED):
|
|
//
|
|
// User has canceled the operation, no massage box.
|
|
//
|
|
return FALSE;
|
|
|
|
default:
|
|
// We assume there was a problem with the file name
|
|
ShowError:
|
|
if (!(uFlags & COF_NOTUSERDRIVEN))
|
|
{
|
|
//
|
|
// REVIEW: Why not use SHSysErrMessageBox?
|
|
//
|
|
ShellMessageBox(hinstCabinet, hwnd,
|
|
MAKEINTRESOURCE(IDS_NOTADIR),
|
|
MAKEINTRESOURCE(IDS_CABINET),
|
|
MB_OK|MB_ICONEXCLAMATION, (LPTSTR)pszPath);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
if (pidl)
|
|
{
|
|
// If we are selecting, we can assume the object exists (since
|
|
// CreateFromPath did not fail), so the parent must be a folder
|
|
if (!(pInfo->uFlags&COF_SELECT) && !(gfInOut&SFGAO_FOLDER))
|
|
{
|
|
ILFree(pidl);
|
|
goto ShowError;
|
|
}
|
|
|
|
pInfo->pidl = pidl;
|
|
pInfo->uFlags |= COF_NORMAL | COF_WAITFORPENDING;
|
|
fSuccess = Cabinet_OpenFolder(pInfo);
|
|
ILFree(pidl);
|
|
// Set back to NULL so we do not free on the way out
|
|
pInfo->pidl = NULL;
|
|
|
|
// We restore this so the caller can free it
|
|
pInfo->pszPath = (LPTSTR)pcszPath;
|
|
}
|
|
else
|
|
{
|
|
goto ShowError;
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
BOOL ExploreUsingOtherInstance(LPCITEMIDLIST pidl, PNEWFOLDERINFO pInfo)
|
|
{
|
|
UINT uFlags = pInfo->uFlags;
|
|
BOOL fReturn = FALSE;
|
|
if (uFlags & COF_EXPLORE) {
|
|
HWND hwnd;
|
|
|
|
hwnd = GetForegroundWindow();
|
|
if (hwnd) {
|
|
if (Cabinet_IsExplorerWindow(hwnd) && Desktop_IsSameRoot(hwnd,NULL))
|
|
{
|
|
HANDLE hPath;
|
|
DWORD dwProcId;
|
|
|
|
GetWindowThreadProcessId(hwnd, &dwProcId);
|
|
hPath = SHAllocShared(pidl, ILGetSize(pidl), dwProcId);
|
|
|
|
if (hPath)
|
|
{
|
|
if (uFlags&COF_SELECT)
|
|
{
|
|
fReturn = SendMessage(hwnd, CWM_SETPATH, 0, (LPARAM)hPath);
|
|
if (fReturn)
|
|
{
|
|
FileCabinet_SelectItem(hwnd, SVSI_SELFLAGS,
|
|
pInfo->pidlSelect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No need to wait for the window to update if we don't
|
|
// have anything to select
|
|
fReturn = SendMessage(hwnd, CWM_SETPATH, CSP_REPOST, (LPARAM)hPath);
|
|
}
|
|
DebugMsg(DM_TRACE, TEXT("ca TR - EUOI CWM_SETPATH returned (%d)"), fReturn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return fReturn;
|
|
}
|
|
|
|
|
|
void Cabinet_CatParam(LPTSTR pszParams, LPCTSTR pszThisParam)
|
|
{
|
|
lstrcat(pszParams, c_szComma);
|
|
lstrcat(pszParams, pszThisParam);
|
|
}
|
|
|
|
|
|
void Cabinet_FlagsToParams(UINT uFlags, LPTSTR pszParams)
|
|
{
|
|
if (uFlags&COF_EXPLORE)
|
|
{
|
|
Cabinet_CatParam(pszParams, c_szExploreSwitch);
|
|
}
|
|
|
|
if (uFlags&COF_SELECT)
|
|
{
|
|
Cabinet_CatParam(pszParams, c_szSelectSwitch);
|
|
}
|
|
|
|
if (uFlags&COF_CREATENEWWINDOW)
|
|
{
|
|
Cabinet_CatParam(pszParams, c_szForceNewWindowSwitch);
|
|
}
|
|
|
|
if (uFlags&COF_USEOPENSETTINGS)
|
|
{
|
|
Cabinet_CatParam(pszParams, c_szUseOpenSettingsSwitch);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Open a folder specified by the command line.
|
|
// Returns TRUE if a new folder window was created.
|
|
// Returns FALSE if an existing folder window was activated or an error
|
|
// occured.
|
|
BOOL Cabinet_OpenFolder(PNEWFOLDERINFO pInfo)
|
|
{
|
|
HWND hwnd = pInfo->hwndCaller;
|
|
LPITEMIDLIST pidl;
|
|
UINT uFlags = pInfo->uFlags;
|
|
int nCmdShow = pInfo->nShow;
|
|
|
|
BOOL fFolderOpened = FALSE;
|
|
LPITEMIDLIST pidlLog = NULL;
|
|
|
|
pInfo->pidlSelect = NULL;
|
|
|
|
// Translate the IDList if necessary to show desktop dirs in their
|
|
// proper place in the tree.
|
|
// If COF_NEWROOT is set, this must already be translated
|
|
if (!(uFlags&(COF_NEWROOT|COF_NOTRANSLATE)) && !OTTranslateIDList(pInfo->pidl, &pidlLog))
|
|
{
|
|
if (!OTIsDesktopRoot() && (uFlags&COF_CHANGEROOTOK))
|
|
{
|
|
TCHAR szExe[MAX_PATH];
|
|
TCHAR szParams[MAX_PATH];
|
|
SHELLEXECUTEINFO ExecInfo;
|
|
HANDLE hIdList;
|
|
|
|
GetModuleFileName(hinstCabinet, szExe, ARRAYSIZE(szExe));
|
|
|
|
if (pInfo->pidl)
|
|
{
|
|
hIdList = SHAllocShared(pInfo->pidl,ILGetSize(pInfo->pidl),GetCurrentProcessId());
|
|
wsprintf(szParams, c_szIDListParam, c_szIDListSwitch, hIdList, GetCurrentProcessId());
|
|
if (!hIdList)
|
|
return FALSE; // We're bad off if we can't alloc
|
|
}
|
|
else
|
|
{
|
|
wsprintf(szParams, TEXT("%s,:0"), c_szIDListSwitch);
|
|
}
|
|
|
|
Cabinet_FlagsToParams(uFlags, szParams+lstrlen(szParams));
|
|
|
|
FillExecInfo(ExecInfo, NULL, NULL, szExe, szParams, NULL, nCmdShow);
|
|
if (!ShellExecuteEx(&ExecInfo))
|
|
SHFreeShared(hIdList,GetCurrentProcessId());
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
if (pidlLog)
|
|
{
|
|
pidl = pidlLog;
|
|
}
|
|
else
|
|
{
|
|
pidl = ILClone(pInfo->pidl);
|
|
if (!pidl)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
if (uFlags & COF_SELECT)
|
|
{
|
|
LPITEMIDLIST pidlLast = ILFindLastID(pidl);
|
|
|
|
pInfo->pidlSelect = ILClone(pidlLast);
|
|
if (!pInfo->pidlSelect)
|
|
{
|
|
// If we couldn't allocate this, we will still attempt to open
|
|
// the folder, but it will probably fail soon enough anyway
|
|
pInfo->uFlags = uFlags = uFlags & (~COF_SELECT);
|
|
}
|
|
pidlLast->mkid.cb = 0;
|
|
}
|
|
|
|
// Check if we need to create a new window or not.
|
|
if (!(uFlags & COF_CREATENEWWINDOW)
|
|
&& (ExploreUsingOtherInstance(pidl, pInfo) ||
|
|
ActivateOrResetOtherInstance(pidl, pInfo)))
|
|
{
|
|
// We must have opened the folder using a different window, so return
|
|
// success
|
|
fFolderOpened = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Create a new folder window
|
|
DWORD idThread;
|
|
HANDLE hThread;
|
|
|
|
PNEWFOLDERINFO pFolderInfo = (PNEWFOLDERINFO)GlobalAlloc(GPTR, SIZEOF(NEWFOLDERINFO));
|
|
|
|
if (!pFolderInfo)
|
|
{
|
|
goto Error1;
|
|
}
|
|
|
|
*pFolderInfo = *pInfo;
|
|
|
|
pFolderInfo->uFlags = uFlags & COF_OPENMASK;
|
|
pFolderInfo->pidl = pidl;
|
|
|
|
// Note that at this point, this->pidl is either NULL or something
|
|
// Alloc'ed especially for us
|
|
|
|
// Add this pidl to the opening folder list
|
|
if (OFLFindOrAdd(pFolderInfo->pidl))
|
|
{
|
|
// Looks like it is already opening...
|
|
fFolderOpened = TRUE;
|
|
goto Error1;
|
|
}
|
|
|
|
if (hwnd)
|
|
SetAppStartingCursor(hwnd, TRUE);
|
|
hThread = CreateThread(NULL, 0, _ThreadInit, pFolderInfo, 0, &idThread);
|
|
if (hThread)
|
|
{
|
|
// Do not free these below
|
|
pidl = NULL;
|
|
pInfo->pidlSelect = NULL;
|
|
|
|
CloseHandle(hThread);
|
|
fFolderOpened = TRUE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
GetLastError();
|
|
#endif
|
|
// NB Normally the child thread will clean this up for us.
|
|
OFLRemove(pFolderInfo->pidl);
|
|
|
|
GlobalFree((HGLOBAL)pFolderInfo);
|
|
// NB Normally the child thread will send us a message when
|
|
// they're done.
|
|
if (hwnd)
|
|
SetAppStartingCursor(hwnd, FALSE);
|
|
}
|
|
}
|
|
|
|
Error1:
|
|
if (pidl)
|
|
{
|
|
ILFree(pidl);
|
|
}
|
|
if (pInfo->pidlSelect)
|
|
{
|
|
ILFree(pInfo->pidlSelect);
|
|
}
|
|
|
|
return fFolderOpened;
|
|
}
|
|
|
|
DWORD StartupInfo_GetHotkey(void)
|
|
{
|
|
STARTUPINFO si;
|
|
|
|
si.cb = SIZEOF(si);
|
|
GetStartupInfo(&si);
|
|
return (DWORD)si.hStdInput;
|
|
}
|
|
|
|
// try to create this by sending a wm_command directly to
|
|
// the desktop.
|
|
BOOL CreateFromDesktop(HINSTANCE hInst, LPCTSTR lpszCmdLine, int nCmdShow)
|
|
{
|
|
PNEWFOLDERINFO psCmdCopy;
|
|
TCHAR szTemp[MAX_PATH];
|
|
BOOL bRet = FALSE;
|
|
const CLSID *pclsid;
|
|
HWND hwndDesktop;
|
|
|
|
psCmdCopy = Alloc(SIZEOF(NEWFOLDERINFO));
|
|
if (!psCmdCopy)
|
|
{
|
|
ShellMessageBox(hInst, NULL,
|
|
MAKEINTRESOURCE(IDS_OUTOFMEM),
|
|
MAKEINTRESOURCE(IDS_CABINET),
|
|
MB_OK|MB_ICONEXCLAMATION);
|
|
return(FALSE);
|
|
}
|
|
|
|
psCmdCopy->nShow = nCmdShow;
|
|
|
|
if (!Parse_CmdLine(lpszCmdLine, psCmdCopy))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
pclsid = psCmdCopy->uFlags&COF_ROOTCLASS ? &psCmdCopy->clsid : NULL;
|
|
|
|
if (psCmdCopy->uFlags & COF_NEWROOT)
|
|
{
|
|
g_CabState.fNotShell = TRUE;
|
|
|
|
if (psCmdCopy->pszRoot)
|
|
{
|
|
LPITEMIDLIST pidlTemp;
|
|
|
|
Assert(!psCmdCopy->pidlRoot);
|
|
|
|
pidlTemp = ILCreateFromPath(psCmdCopy->pszRoot);
|
|
if (!pidlTemp)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
psCmdCopy->pidlRoot = ILGlobalClone(pidlTemp);
|
|
ILFree(pidlTemp);
|
|
if (!psCmdCopy->pidlRoot)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
Str_SetPtr(&psCmdCopy->pszRoot, NULL);
|
|
}
|
|
}
|
|
|
|
hwndDesktop = FindRootedDesktop(pclsid, psCmdCopy->pidlRoot);
|
|
|
|
if (hwndDesktop)
|
|
{
|
|
HNFBLOCK hBlock;
|
|
DWORD dwProcId;
|
|
DWORD dwThreadId;
|
|
|
|
// If there is already a desktop window with this root, use it
|
|
// Note we check !pidl so if nothing was specified, we will
|
|
// open in the curdir
|
|
if (!(psCmdCopy->uFlags & COF_NEWROOT) && !psCmdCopy->pidl)
|
|
{
|
|
// Take care of relative or empty paths
|
|
GetCurrentDirectory(ARRAYSIZE(szTemp), szTemp);
|
|
PathCombine(szTemp, szTemp, psCmdCopy->pszPath);
|
|
Str_SetPtr(&psCmdCopy->pszPath, szTemp);
|
|
}
|
|
|
|
// Set it's hotkey from our startup info.
|
|
psCmdCopy->dwHotKey = StartupInfo_GetHotkey();
|
|
|
|
dwThreadId = GetWindowThreadProcessId(hwndDesktop, &dwProcId);
|
|
hBlock = ConvertNFItoHNFBLOCK(psCmdCopy,dwProcId);
|
|
|
|
#ifdef BOBDAY_TESTING_FOCUS_CHANGES
|
|
//
|
|
// We attach and detach to allow it to steal our foregroundness
|
|
//
|
|
AttachThreadInput(dwThreadId, GetCurrentThreadId(), TRUE);
|
|
#endif
|
|
SendMessage(hwndDesktop, CWM_COMMANDLINE, 0, (LPARAM)hBlock);
|
|
#ifdef BOBDAY_TESTING_FOCUS_CHANGES
|
|
AttachThreadInput(dwThreadId, GetCurrentThreadId(), FALSE);
|
|
#endif
|
|
|
|
goto CleanUp;
|
|
}
|
|
else if (!g_CabState.fNotShell)
|
|
{
|
|
if (!g_fRunSeparateDesktop) // Ok to not find desktop in separate process case
|
|
{
|
|
ShellMessageBox(hInst, NULL,
|
|
MAKEINTRESOURCE(IDS_NOTINITED),
|
|
MAKEINTRESOURCE(IDS_CABINET),
|
|
MB_OK|MB_ICONEXCLAMATION);
|
|
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
if (!(psCmdCopy->uFlags & COF_NEWROOT))
|
|
{
|
|
if (!Cabinet_CreateAppGlobals(NULL, NULL)) // this MUST come after FirstInstance() check
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// this MUST come after FirstInstance() check
|
|
if (!Cabinet_CreateAppGlobals(pclsid, psCmdCopy->pidlRoot))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
if (!CreateProxyDesktop(hInst, pclsid, psCmdCopy->pidlRoot))
|
|
{
|
|
// I really don't want to continue if I cannot create this window
|
|
goto CleanUp;
|
|
}
|
|
|
|
// I'm setting fNotShell to indicate this is not really a Shell window.
|
|
// This should keep me from closing other windows when we are all done
|
|
g_CabState.fNotShell = TRUE;
|
|
|
|
SHSetInstanceExplorer(&g_unkRef);
|
|
|
|
#ifdef WINNT
|
|
PostMessage(v_hwndDesktop, WM_SHELLNOTIFY, SHELLNOTIFY_OLELOADED, 0);
|
|
#endif
|
|
|
|
// We want the global hwndDesktop that we just created
|
|
psCmdCopy->hwndCaller = v_hwndDesktop;
|
|
bRet = Cabinet_OpenFolderPath(psCmdCopy);
|
|
|
|
CleanUp:
|
|
Cabinet_CleanUpCommand(psCmdCopy);
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
|
|
#ifdef METRICS_SANE_DEFAULTS
|
|
BOOL g_fDragFullWindows=FALSE;
|
|
int g_cxEdge = 2;
|
|
int g_cyEdge = 2;
|
|
int g_cySize = 18;
|
|
int g_cyTabSpace = 3;
|
|
int g_cxSizeFrame = 2;
|
|
int g_cxBorder = 1;
|
|
int g_cyBorder = 1;
|
|
int g_cxIcon = 32;
|
|
int g_cyIcon = 32;
|
|
int g_cxSmIcon = 16;
|
|
int g_cySmIcon = 16;
|
|
int g_cxDlgFrame = 2;
|
|
int g_cyDlgFrame = 2;
|
|
int g_cxFrame = 2;
|
|
int g_cyFrame = 2;
|
|
//int g_cySmCaption = 10;
|
|
int g_cxMinimized = 30;
|
|
int g_fCleanBoot = FALSE;
|
|
#else // METRICS_SANE_DEFAULTS
|
|
|
|
BOOL g_fDragFullWindows=FALSE;
|
|
int g_cxEdge=0;
|
|
int g_cyEdge=0;
|
|
int g_cySize=0;
|
|
int g_cyTabSpace=0;
|
|
int g_cxSizeFrame=0;
|
|
int g_cxBorder=0;
|
|
int g_cyBorder=0;
|
|
int g_cxScreen=0;
|
|
int g_cyScreen=0;
|
|
int g_cxIcon=0;
|
|
int g_cyIcon=0;
|
|
int g_cxSmIcon=0;
|
|
int g_cySmIcon=0;
|
|
int g_cxDlgFrame=0;
|
|
int g_cyDlgFrame=0;
|
|
int g_cxFrame=0;
|
|
int g_cyFrame=0;
|
|
//int g_cySmCaption=0;
|
|
int g_cxMinimized=0;
|
|
int g_cxWorkArea=0;
|
|
int g_cyWorkArea=0;
|
|
int g_fCleanBoot=0;
|
|
int g_cxVScroll=0;
|
|
int g_cyHScroll=0;
|
|
#endif // METRICS_SANE_DEFAULTS
|
|
|
|
void InvalidateImageIndices()
|
|
{
|
|
TCHAR szFile[MAX_PATH];
|
|
|
|
// get the default image indices
|
|
|
|
GetModuleFileName(hinstCabinet, szFile, ARRAYSIZE(szFile));
|
|
g_iTreeUpIndex = Shell_GetCachedImageIndex(szFile, ICO_TREEUP - ICO_FIRST, 0);
|
|
g_nDefOpenSysIndex = Shell_GetCachedImageIndex(c_szShell2, II_FOLDEROPEN, 0);
|
|
g_nDefNormalSysIndex = Shell_GetCachedImageIndex(c_szShell2, II_FOLDER, 0);
|
|
}
|
|
|
|
void Cabinet_InitGlobalMetrics(WPARAM wParam, LPTSTR lpszSection)
|
|
{
|
|
BOOL fForce = (!lpszSection || !*lpszSection);
|
|
|
|
if (fForce || wParam == SPI_SETDRAGFULLWINDOWS) {
|
|
SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &g_fDragFullWindows, 0);
|
|
}
|
|
|
|
if (fForce || !lstrcmpi(lpszSection, c_szMetrics) ||
|
|
wParam == SPI_SETNONCLIENTMETRICS) {
|
|
|
|
// REVIEW, before it's all over, make sure all these vars are used somewhere.
|
|
g_cxEdge = GetSystemMetrics(SM_CXEDGE);
|
|
g_cyEdge = GetSystemMetrics(SM_CYEDGE);
|
|
g_cyTabSpace = (g_cyEdge * 3) / 2; // cause the graphic designers really really want 3.
|
|
g_cySize = GetSystemMetrics(SM_CYSIZE);
|
|
g_cxSizeFrame = GetSystemMetrics(SM_CXSIZEFRAME);
|
|
g_cxBorder = GetSystemMetrics(SM_CXBORDER);
|
|
g_cyBorder = GetSystemMetrics(SM_CYBORDER);
|
|
g_cxVScroll = GetSystemMetrics(SM_CXVSCROLL);
|
|
g_cyHScroll = GetSystemMetrics(SM_CYHSCROLL);
|
|
g_cxScreen = GetSystemMetrics(SM_CXSCREEN);
|
|
g_cyScreen = GetSystemMetrics(SM_CYSCREEN);
|
|
g_cxDlgFrame = GetSystemMetrics(SM_CXDLGFRAME);
|
|
g_cyDlgFrame = GetSystemMetrics(SM_CYDLGFRAME);
|
|
g_cxFrame = GetSystemMetrics(SM_CXFRAME);
|
|
g_cyFrame = GetSystemMetrics(SM_CYFRAME);
|
|
g_cxMinimized = GetSystemMetrics(SM_CXMINIMIZED);
|
|
|
|
FileIconInit(TRUE); // Tell the shell we want to play with a full deck
|
|
|
|
Shell_GetImageLists(&g_himlSysLarge, &g_himlSysSmall);
|
|
ImageList_GetIconSize(g_himlSysLarge, &g_cxIcon, &g_cyIcon);
|
|
ImageList_GetIconSize(g_himlSysSmall, &g_cxSmIcon, &g_cySmIcon);
|
|
|
|
InvalidateImageIndices();
|
|
|
|
//BUGBUG mabey we should check if the icon cache realy changed size etc.
|
|
OTInvalidateAll();
|
|
}
|
|
|
|
if (fForce || !lstrcmpi(lpszSection, c_szMetrics) ||
|
|
wParam == SPI_SETWORKAREA) {
|
|
|
|
RECT rcWorkArea;
|
|
SystemParametersInfo(SPI_GETWORKAREA, FALSE, &rcWorkArea, 0);
|
|
g_cxWorkArea = rcWorkArea.right - rcWorkArea.left;
|
|
g_cyWorkArea = rcWorkArea.bottom - rcWorkArea.top;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
BOOL Cabinet_CreateAppGlobals(const CLSID *pclsid,
|
|
LPCITEMIDLIST pidlRoot)
|
|
{
|
|
DWORD cbData;
|
|
BOOL bRet;
|
|
SHELLSTATE ss;
|
|
|
|
Cabinet_InitGlobalMetrics(0, NULL);
|
|
|
|
g_fNoDesktop = SHRestricted(REST_NODESKTOP);
|
|
|
|
// Shell32.Dll now exports a function to read the cabinet state, therefore we can call it
|
|
// to do the work for us. The only catch is that we write the stack back into the registry
|
|
// if we failed to read it - the read will have initialized the structure to its default state.
|
|
|
|
if ( !ReadCabinetState( &g_CabState, SIZEOF(g_CabState) ) )
|
|
{
|
|
DebugMsg( DM_TRACE, TEXT( "initcab: Failed to read the cabinet state" ) );
|
|
WriteCabinetState( &g_CabState );
|
|
}
|
|
|
|
SHGetSetSettings(&ss, SSF_SHOWCOMPCOLOR, FALSE);
|
|
g_fShowCompColor = ss.fShowCompColor;
|
|
|
|
bRet = OneTree_Initialize(pclsid, pidlRoot); // needs to be before code below, since it creats globals
|
|
|
|
// Don't use g_nDefOpenSysIndex until one tree initializes it!
|
|
g_hIconDefOpenLarge = ImageList_ExtractIcon(hinstCabinet, g_himlSysLarge, g_nDefOpenSysIndex);
|
|
g_hIconDefOpenSmall = ImageList_ExtractIcon(hinstCabinet, g_himlSysSmall, g_nDefOpenSysIndex);
|
|
|
|
InitDefaultFolderSettings();
|
|
return(bRet);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns TRUE of this is the first time the cabinet has been run. False
|
|
// otherwise.
|
|
BOOL FirstInstance()
|
|
{
|
|
extern const TCHAR c_szOTClass[];
|
|
|
|
// Overide the first instance check.
|
|
// BUGBUG:: This is set *after* this call so this won't work!
|
|
if (g_CabState.fNotShell)
|
|
return FALSE;
|
|
|
|
// We need to be careful on which window we look for. If we look for
|
|
// our desktop window class and Progman is running we will find the
|
|
// progman window. So Instead we should ask user for the shell window.
|
|
|
|
// We can not depend on any values being set here as this is the
|
|
// start of a new process. This wont be called when we start new
|
|
// threads.
|
|
if (GetShellWindow()
|
|
|| FindWindow(c_szProxyDesktopClass, NULL)
|
|
|| FindWindow(c_szOTClass, NULL))
|
|
return FALSE;
|
|
else
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
TCHAR szModuleName[MAX_PATH];
|
|
LPTSTR pszModuleName;
|
|
LPTSTR pszT;
|
|
LPTSTR pszPortion;
|
|
LPTSTR pszComma;
|
|
|
|
// See if the Shell= line is set to something other than us.
|
|
// If so we will go into non-primary mode
|
|
GetModuleFileName(NULL, szModuleName, ARRAYSIZE(szModuleName));
|
|
pszModuleName = PathFindFileName(szModuleName);
|
|
|
|
GetPrivateProfileString(c_szBoot, c_szShell, pszModuleName,
|
|
szBuffer, ARRAYSIZE(szBuffer), c_szSystemIni);
|
|
|
|
|
|
pszPortion = szBuffer;
|
|
|
|
do {
|
|
pszComma = StrChr(pszPortion,TEXT(','));
|
|
if (pszComma)
|
|
*pszComma = TEXT('\0'); // Separate at the commas
|
|
|
|
// Remove any arguments from the command line
|
|
pszT = PathGetArgs(pszPortion);
|
|
if (*pszT)
|
|
*(pszT-1) = TEXT('\0'); // Clober the blank...
|
|
else
|
|
{
|
|
pszT = CharPrev(pszPortion, pszT);
|
|
if (*pszT == TEXT(' '))
|
|
*pszT = TEXT('\0');
|
|
}
|
|
|
|
// Now find the last component of the name
|
|
pszT = PathFindFileName(pszPortion);
|
|
|
|
// Now see if it is us.
|
|
if (lstrcmpi(pszT, pszModuleName) == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
// NB Special case shell=install.exe - assume we are the shell.
|
|
// Symantec un-installers temporarily set shell=installer.exe so
|
|
// we think we're not the shell when we are. They fail to clean up
|
|
// a bunch of links if we don't do this.
|
|
else if (lstrcmpi(pszT, c_szInstallExe) == 0)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("c.fi: Shell=Install.exe in win.ini - assuming we are really the shell."));
|
|
return TRUE;
|
|
}
|
|
|
|
pszPortion = pszComma + 1;
|
|
|
|
} while (pszComma != NULL);
|
|
|
|
g_CabState.fNotShell = TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct _FOLDERWINDOWENUM
|
|
{
|
|
LPCTSTR pszClass;
|
|
BOOL bThisInstOnly;
|
|
DWORD idProc;
|
|
} FOLDERWINDOWENUM, *LPFOLDERWINDOWENUM;
|
|
|
|
|
|
BOOL CALLBACK _CloseAllEnumProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
LPFOLDERWINDOWENUM pcae = (LPFOLDERWINDOWENUM)lParam;
|
|
TCHAR szClass[MAX_PATH];
|
|
|
|
if (pcae->bThisInstOnly)
|
|
{
|
|
DWORD idProc;
|
|
|
|
GetWindowThreadProcessId(hwnd, &idProc);
|
|
if (idProc != pcae->idProc)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
|
|
if (!lstrcmpi(szClass, pcae->pszClass))
|
|
{
|
|
// Since we are about to do an ExitProcess, this cannot be a
|
|
// PostMessage
|
|
SendMessage(hwnd, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Close all remaining folder windows.
|
|
void FolderWindows_CloseAll(LPCTSTR pszClass, BOOL bThisInstOnly)
|
|
{
|
|
FOLDERWINDOWENUM cae = { pszClass, bThisInstOnly } ;
|
|
|
|
if (bThisInstOnly)
|
|
{
|
|
cae.idProc = GetCurrentProcessId();
|
|
}
|
|
|
|
EnumWindows(_CloseAllEnumProc, (LPARAM)(LPFOLDERWINDOWENUM)&cae);
|
|
}
|
|
|
|
#ifdef CONVERT_RESTRICTIONS
|
|
//----------------------------------------------------------------------------
|
|
BOOL WINAPI Reg_SetDWord(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, DWORD dw)
|
|
{
|
|
return Reg_SetStruct(hkey, pszSubKey, pszValue, &dw, SIZEOF(dw));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const TCHAR c_szExplorer[] = TEXT("Explorer");
|
|
const TCHAR c_szRestrictions[] = TEXT("Restrictions");
|
|
const TCHAR c_szEditLevel[] = TEXT("EditLevel");
|
|
const TCHAR c_szNoRun[] = TEXT("NoRun");
|
|
const TCHAR c_szNoClose[] = TEXT("NoClose");
|
|
const TCHAR c_szNoSaveSettings[] = TEXT("NoSaveSettings");
|
|
const TCHAR c_szNoFileMenu[] = TEXT("NoFileMenu");
|
|
|
|
//---------------------------------------------------------------------------
|
|
BOOL FindProgmanIni(LPTSTR pszPath, int cbPath)
|
|
{
|
|
|
|
Assert(pszPath)
|
|
|
|
GetWindowsDirectory(pszPath, cbPath);
|
|
PathAppend(pszPath, c_szProgmanIni);
|
|
|
|
// this should work for an upgrade
|
|
if (PathFileExists(pszPath))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
DebugMsg(DM_ERROR, TEXT("Can't find progman.ini"));
|
|
return FALSE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Convert progman restrictions. GrpConv can't easily do this since this is
|
|
// user data specific to the primary user.
|
|
void Restrictions_Convert(void)
|
|
{
|
|
DWORD dw;
|
|
TCHAR szIniFile[MAX_PATH];
|
|
HKEY hkeyPolicies;
|
|
|
|
// DebugMsg(DM_TRACE, "c.cr: Converting restrictions...");
|
|
|
|
if (FindProgmanIni(szIniFile, ARRAYSIZE(szIniFile)))
|
|
{
|
|
if (RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_POLICIES, &hkeyPolicies) == ERROR_SUCCESS)
|
|
{
|
|
// Get them. Set them.
|
|
dw = GetPrivateProfileInt(c_szRestrictions, c_szEditLevel, 0, szIniFile);
|
|
Reg_SetDWord(hkeyPolicies, c_szExplorer, c_szEditLevel, dw);
|
|
|
|
dw = GetPrivateProfileInt(c_szRestrictions, c_szNoRun, 0, szIniFile);
|
|
Reg_SetDWord(hkeyPolicies, c_szExplorer, c_szNoRun, dw);
|
|
|
|
dw = GetPrivateProfileInt(c_szRestrictions, c_szNoClose, 0, szIniFile);
|
|
Reg_SetDWord(hkeyPolicies, c_szExplorer, c_szNoClose, dw);
|
|
|
|
dw = GetPrivateProfileInt(c_szRestrictions, c_szNoSaveSettings , 0, szIniFile);
|
|
Reg_SetDWord(hkeyPolicies, c_szExplorer, c_szNoSaveSettings, dw);
|
|
|
|
dw = GetPrivateProfileInt(c_szRestrictions, c_szNoFileMenu , 0, szIniFile);
|
|
Reg_SetDWord(hkeyPolicies, c_szExplorer, c_szNoFileMenu, dw);
|
|
|
|
RegCloseKey(hkeyPolicies);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("c.cr: Unable to create policy key for registry."));
|
|
DebugMsg(DM_ERROR, TEXT("c.cr: Restrictions can not be converted."));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
void DisplayCleanBootMsg()
|
|
{
|
|
TCHAR szMsg[1024];
|
|
TCHAR szTitle[80];
|
|
int ids;
|
|
int cb;
|
|
LPTSTR pszMsg = szMsg;
|
|
|
|
szMsg[0] = TEXT('\0');
|
|
|
|
for (ids=IDS_CLEANBOOTMSG1; ids <= IDS_CLEANBOOTMSG4 ; ids++)
|
|
{
|
|
cb = LoadString(hinstCabinet, ids, pszMsg,
|
|
ARRAYSIZE(szMsg) - (int)(pszMsg - szMsg));
|
|
if (cb == 0)
|
|
break;
|
|
pszMsg += cb;
|
|
}
|
|
// Make sure it is NULL terminated
|
|
*pszMsg = TEXT('\0');
|
|
|
|
LoadString(hinstCabinet, IDS_DESKTOP, szTitle, ARRAYSIZE(szTitle));
|
|
|
|
// Now display the message.
|
|
MessageBox(NULL, szMsg, szTitle,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
const CHAR c_szTimeChangedRunDLL[] = "rundll32 shell32.dll,Control_RunDLL timedate.cpl,,/m";
|
|
const TCHAR c_szTimeChangedRunOnce[] = TEXT("WarnTimeChanged");
|
|
|
|
void Cabinet_DoDaylightCheck(BOOL fStartupInit)
|
|
{
|
|
DWORD changed;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("c.ddc(%d): calling k32.rdi"), fStartupInit);
|
|
|
|
#ifdef WINNT
|
|
changed = FALSE;
|
|
#else
|
|
// Win95 base does not automatically handle timezone cutover
|
|
// we have poke it every so often...
|
|
changed = RefreshDaylightInformation(TRUE);
|
|
#endif
|
|
|
|
if (changed > 0)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("c.ddc(%d): rdi changed - %lu"), fStartupInit, changed);
|
|
|
|
// something actually changed, tell everbody
|
|
if (!fStartupInit)
|
|
{
|
|
SendMessage((HWND)-1, WM_TIMECHANGE, 0, 0);
|
|
|
|
// if the local time changed tell the user
|
|
if (changed > 1)
|
|
WinExec(c_szTimeChangedRunDLL, SW_SHOWNORMAL);
|
|
}
|
|
else
|
|
{
|
|
// there should only be "server" processes around anyway
|
|
PostMessage((HWND)-1, WM_TIMECHANGE, 0, 0);
|
|
|
|
// if the local time changed queue a runonce to tell the user
|
|
if (changed > 1)
|
|
{
|
|
HKEY runonce;
|
|
|
|
if (RegCreateKey(HKEY_LOCAL_MACHINE, c_szRunOnce, &runonce) ==
|
|
ERROR_SUCCESS)
|
|
{
|
|
RegSetValueEx(runonce, (LPCTSTR)c_szTimeChangedRunOnce,
|
|
0UL, REG_SZ, (LPBYTE)c_szTimeChangedRunDLL,
|
|
(DWORD)(lstrlenA(c_szTimeChangedRunDLL) + 1));
|
|
|
|
RegCloseKey(runonce);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void _HandleCmdLine(LPTSTR lpszCmdLine, UINT nCmdShow)
|
|
{
|
|
LPTSTR lpszArgs;
|
|
SHELLEXECUTEINFO ExecInfo;
|
|
|
|
// run the cmd line passed up from win.com
|
|
if (lpszCmdLine && *lpszCmdLine)
|
|
{
|
|
lpszArgs = PathGetArgs(lpszCmdLine);
|
|
if (*lpszArgs)
|
|
*(lpszArgs-1) = TEXT('\0');
|
|
FillExecInfo(ExecInfo, NULL, NULL, lpszCmdLine, lpszArgs, NULL, nCmdShow);
|
|
ShellExecuteEx(&ExecInfo);
|
|
}
|
|
}
|
|
|
|
// stolen from the CRT, used to shirink our code
|
|
|
|
HANDLE g_hProcessHeap = NULL;
|
|
|
|
int _stdcall ModuleEntry(void)
|
|
{
|
|
int i;
|
|
STARTUPINFOA si;
|
|
LPTSTR pszCmdLine = GetCommandLine();
|
|
|
|
g_hProcessHeap = GetProcessHeap();
|
|
|
|
//
|
|
// We don't want the "No disk in drive X:" requesters, so we set
|
|
// the critical error mask such that calls will just silently fail
|
|
//
|
|
|
|
SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
if ( *pszCmdLine == TEXT('\"') ) {
|
|
/*
|
|
* Scan, and skip over, subsequent characters until
|
|
* another double-quote or a null is encountered.
|
|
*/
|
|
while ( *++pszCmdLine && (*pszCmdLine
|
|
!= TEXT('\"')) );
|
|
/*
|
|
* If we stopped on a double-quote (usual case), skip
|
|
* over it.
|
|
*/
|
|
if ( *pszCmdLine == TEXT('\"') )
|
|
pszCmdLine++;
|
|
}
|
|
else {
|
|
while (*pszCmdLine > TEXT(' '))
|
|
pszCmdLine++;
|
|
}
|
|
|
|
/*
|
|
* Skip past any white space preceeding the second token.
|
|
*/
|
|
while (*pszCmdLine && (*pszCmdLine <= TEXT(' '))) {
|
|
pszCmdLine++;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* read wDebugMask entry from win.ini for EXPLORER.EXE.
|
|
* The default is 0x000E, which includes DM_WARNING, DM_ERROR,
|
|
* and DM_ASSERT. The default has DM_TRACE and DM_ALLOC turned
|
|
* off.
|
|
*/
|
|
{
|
|
CHAR aszDebugMask[ 80 ];
|
|
|
|
if (GetProfileStringA( "Explorer", "DebugMask", "0x000E",
|
|
aszDebugMask, ARRAYSIZE(aszDebugMask)) > 0 )
|
|
{
|
|
char *p;
|
|
p = aszDebugMask;
|
|
if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) {
|
|
p += 2;
|
|
}
|
|
wDebugMask = 0;
|
|
while (*p) {
|
|
if (*p >= '0' && *p <= '9') {
|
|
wDebugMask = wDebugMask * 16 + (*p - '0');
|
|
} else {
|
|
if (*p >= 'A' && *p <= 'F') {
|
|
wDebugMask = wDebugMask * 16 + (*p - 'A' + 10);
|
|
} else {
|
|
if (*p >= 'a' && *p <= 'f') {
|
|
wDebugMask = wDebugMask * 16 + (*p - 'a' + 10);
|
|
}
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
si.dwFlags = 0;
|
|
GetStartupInfoA(&si);
|
|
|
|
i = WinMainT(GetModuleHandle(NULL), NULL, pszCmdLine,
|
|
si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
|
|
|
|
// Since we now have a way for an extension to tell us when it is finished,
|
|
// we will terminate all processes when the main thread goes away.
|
|
|
|
ExitProcess(i);
|
|
|
|
return i;
|
|
}
|
|
|
|
TCHAR const c_szDesktopProcess[] = TEXT("DesktopProcess");
|
|
|
|
//---------------------------------------------------------------------------
|
|
int WinMainT(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpszCmdLine, int nCmdShow)
|
|
{
|
|
MSG msg;
|
|
// DWORD dwTime = GetTickCount();
|
|
BOOL bShellInstance;
|
|
|
|
// Very Important: Make sure to init dde prior to any Get/Peek/Wait().
|
|
InitializeCriticalSection(&g_csThreads);
|
|
|
|
g_bIsUserAnAdmin = IsUserAnAdmin();
|
|
|
|
g_msgMSWheel = RegisterWindowMessage(TEXT(MSH_MOUSEWHEEL));
|
|
|
|
hinstCabinet = hInstance;
|
|
|
|
if (RegCreateKey(HKEY_CURRENT_USER, c_szRegExplorer, &g_hkeyExplorer) != ERROR_SUCCESS) {
|
|
DebugMsg(DM_ERROR, TEXT("Really really bad.. unable to create reg explorer key"));
|
|
// Try to keep going??? We'll fault pretty soon otherwise.
|
|
// return;
|
|
}
|
|
|
|
if (SHRestricted(REST_SEPARATEDESKTOPPROCESS))
|
|
g_fRunSeparateDesktop = TRUE;
|
|
else
|
|
{
|
|
DWORD dwType;
|
|
DWORD cbData = SIZEOF(g_fRunSeparateDesktop);
|
|
|
|
RegQueryValueEx(g_hkeyExplorer,c_szDesktopProcess,0,&dwType,
|
|
(LPBYTE)&g_fRunSeparateDesktop, &cbData);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
if (GetAsyncKeyState(VK_MENU) < 0)
|
|
DebugBreak(); // Need to debug...
|
|
#endif
|
|
|
|
// Make sure this is set before we do anythings as many internal
|
|
// functions depend on this value being set!.
|
|
g_fCleanBoot = GetSystemMetrics(SM_CLEANBOOT);
|
|
|
|
// Note if we are not the Shell, we will never be the "FirstInstance"
|
|
bShellInstance = FirstInstance(); // this MUST come before Cabinet_CreateAppGlobals
|
|
if (!bShellInstance) // this MUST come before Cabinet_CreateAppGlobals
|
|
{
|
|
if (CreateFromDesktop(hInstance, lpszCmdLine, nCmdShow))
|
|
{
|
|
//
|
|
// We want this thread to be fully initialized to run
|
|
// non-desktop explorers on all of its own threads.
|
|
//
|
|
PSFCACHE psfc;
|
|
|
|
psfc = SFCInitializeThread();
|
|
if (!psfc)
|
|
goto Error;
|
|
goto ProcessMessages;
|
|
}
|
|
|
|
goto DontProcessMessages;
|
|
}
|
|
else
|
|
{
|
|
BOOL fNoBlowups;
|
|
PSFCACHE psfc;
|
|
BOOL fInitShell;
|
|
|
|
InitialiseDDE();
|
|
|
|
// Specify the shutdown order of the shell process. 2 means
|
|
// the explorer should shutdown after everything but ntsd/windbg
|
|
// (level 0). (Taskman used to use 1, but is no more.)
|
|
|
|
SetProcessShutdownParameters(2, 0);
|
|
|
|
#ifdef WINNT
|
|
_RunTaskMan();
|
|
#endif
|
|
|
|
// NB Make this the primary thread by calling peek message
|
|
// for a message we know we're not going to get.
|
|
// If we don't do it really soon, the notify thread can sometimes
|
|
// become the primary thread by accident. There's a bunch of
|
|
// special code in user to implement DDE hacks by assuming that
|
|
// the primary thread is handling DDE.
|
|
// Also, the PeekMsg() will cause us to set the WaitForInputIdle()
|
|
// event so we better be ready to do all dde.
|
|
DDE_AddShellServices();
|
|
PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE);
|
|
|
|
// This is real crudy, but basically I use our overloaded
|
|
// thunk RegisterShellHook to the 16 bit side if we are the
|
|
// first instance.
|
|
fNoBlowups = RegisterShellHook(NULL, (BOOL)4);
|
|
DebugMsg(DM_TRACE, TEXT("c.First No Blowups? %d"), fNoBlowups);
|
|
|
|
// NOTE: we must do this before creating our main window so
|
|
// we don't deadlock while waiting for startup apps to complete
|
|
if (fNoBlowups)
|
|
{
|
|
// force kernel32 to update the timezone before running any apps
|
|
Cabinet_DoDaylightCheck(TRUE);
|
|
|
|
fInitShell = RunRegApps(HKEY_LOCAL_MACHINE, c_szRunOnce, RRA_DELETE | RRA_WAIT);
|
|
|
|
#ifdef WINNT
|
|
//
|
|
// On NT, we need to figure out the fInitShell on a per-user
|
|
// basis rather than once per machine. We want the welcome
|
|
// splash screen to come up for every new user.
|
|
//
|
|
|
|
{
|
|
TCHAR szInitialTip[] = TEXT("DisplayInitialTipWindow");
|
|
DWORD dwDisp, dwType, dwSize;
|
|
LONG lResult;
|
|
HKEY hkey;
|
|
BOOL bTemp = FALSE;
|
|
|
|
if (RegCreateKeyEx(HKEY_CURRENT_USER,
|
|
g_szRegTips,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&hkey,
|
|
&dwDisp) == ERROR_SUCCESS) {
|
|
|
|
dwSize = sizeof(fInitShell);
|
|
|
|
//
|
|
// Check for the inital flag
|
|
//
|
|
|
|
if (RegQueryValueEx(hkey,
|
|
szInitialTip,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &fInitShell,
|
|
&dwSize) != ERROR_SUCCESS) {
|
|
fInitShell = TRUE;
|
|
}
|
|
|
|
if (fInitShell) {
|
|
|
|
//
|
|
// Turn off the initial tip window for future shell starts.
|
|
//
|
|
|
|
bTemp = FALSE;
|
|
RegSetValueEx (hkey,
|
|
szInitialTip,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE) &bTemp,
|
|
sizeof(bTemp));
|
|
}
|
|
|
|
RegCloseKey (hkey);
|
|
|
|
} else {
|
|
|
|
fInitShell = TRUE;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONVERT_RESTRICTIONS
|
|
if (!fInitShell)
|
|
Restrictions_Convert();
|
|
#endif
|
|
|
|
// Other init stuff before the desktop appears.
|
|
Cabinet_CreateAppGlobals(NULL, NULL); // this MUST come after FirstInstance() check
|
|
|
|
// Handle the cleanboot to display a message for the user
|
|
if (g_fCleanBoot)
|
|
DisplayCleanBootMsg();
|
|
|
|
InitTrayClass(hInstance);
|
|
InitDesktopClass(hInstance);
|
|
|
|
// Create the other special folders.
|
|
CreateShellDirectories();
|
|
|
|
psfc = SFCInitializeThread();
|
|
if (!psfc)
|
|
goto Error;
|
|
|
|
// Creates v_hwndTray as well.
|
|
if (!CreateDesktopWindows(hInstance))
|
|
goto Error2;
|
|
|
|
// DebugMsg(DM_TRACE, "c.wm: Init time %d ms.\n\r", GetTickCount()-dwTime);
|
|
// Other init stuff after the desktop appears.
|
|
#ifdef GRPCONV_AUTORUN
|
|
RunGrpConv();
|
|
#endif
|
|
|
|
if (fNoBlowups && !g_fCleanBoot)
|
|
{
|
|
// We need to send a WM_USER message to HWND_DESKTOP to give user a chance
|
|
// to set it's free resource list...
|
|
//
|
|
SendMessage(GetDesktopWindow(), WM_USER, 0, 0);
|
|
|
|
|
|
// If First boot show welcome first and wait for it to
|
|
// complete before running the rest of the startup apps...
|
|
if (fInitShell)
|
|
{
|
|
if (_RunWelcome(TRUE) == PEEK_QUIT)
|
|
goto DontProcessMessages; // we bailed out...
|
|
|
|
// The user may have choosen to shutdown when welcome was up so don't start apps
|
|
if (!IsWindowVisible(v_hwndDesktop))
|
|
goto ProcessMessages;
|
|
}
|
|
|
|
_DoRunEquals(); // Process the Load= and Run= lines...
|
|
_ExecuteStartupPrograms(v_hwndTray);
|
|
_CreateSavedWindows();
|
|
|
|
RunRegApps(HKEY_CURRENT_USER, c_szRunOnce, RRA_DELETE);
|
|
|
|
// If not first boot run welcome last...
|
|
if (!fInitShell)
|
|
_RunWelcome(FALSE);
|
|
}
|
|
|
|
_HandleCmdLine(lpszCmdLine, nCmdShow);
|
|
|
|
//
|
|
// Now maybe we need to startup the hidden non-desktop but still
|
|
// desktop-rooted explorer...
|
|
//
|
|
if (g_fRunSeparateDesktop && g_fRunSeparateStartAndStay)
|
|
{
|
|
TCHAR szExe[MAX_PATH];
|
|
SHELLEXECUTEINFO ExecInfo;
|
|
|
|
GetModuleFileName(hinstCabinet, szExe, ARRAYSIZE(szExe));
|
|
|
|
FillExecInfo(ExecInfo, NULL, NULL, szExe, c_szNoUISwitch, NULL, SW_SHOWNORMAL);
|
|
ShellExecuteEx(&ExecInfo);
|
|
}
|
|
|
|
ProcessMessages:
|
|
// NOTE: NULL indicates that we default to the desktop but
|
|
// will switch to the tray's PFileCabinet when it gets the activation
|
|
// see desktop.c WM_ACTIVATE for details
|
|
|
|
MessageLoop(NULL);
|
|
|
|
DontProcessMessages:
|
|
if (v_hwndTray)
|
|
{
|
|
DestroyWindow(v_hwndTray);
|
|
v_hwndTray = NULL;
|
|
}
|
|
|
|
if (v_hwndDesktop)
|
|
{
|
|
DestroyWindow(v_hwndDesktop);
|
|
v_hwndDesktop = NULL;
|
|
}
|
|
SHSetInstanceExplorer(NULL);
|
|
|
|
// This is easier than trying to keep track of all the threads
|
|
// and making sure they get a chance to close before we exit.
|
|
FolderWindows_CloseAll(c_szCabinetClass, !bShellInstance);
|
|
FolderWindows_CloseAll(c_szExploreClass, !bShellInstance);
|
|
Error2:
|
|
SFCTerminateThread();
|
|
}
|
|
|
|
Error:
|
|
|
|
OneTree_Terminate();
|
|
if (bShellInstance)
|
|
{
|
|
DDE_RemoveShellServices();
|
|
UnInitialiseDDE();
|
|
}
|
|
|
|
//
|
|
// Free all unused libraries here.
|
|
//
|
|
SHFreeUnusedLibraries();
|
|
|
|
DebugMsg(DM_TRACE, TEXT("c.App Exit."));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void FileCabinet_SelectItem(HWND hwnd, UINT uFlags, LPCITEMIDLIST pidlSelect)
|
|
{
|
|
HANDLE hData = SHAllocShared(pidlSelect, ILGetSize(pidlSelect), GetCurrentProcessId());
|
|
|
|
if (hData)
|
|
{
|
|
SendMessage(hwnd, CWM_SELECTITEM, uFlags, (LPARAM)hData);
|
|
}
|
|
}
|