Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2101 lines
63 KiB

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1992
//
// File: desktop.c
//
//---------------------------------------------------------------------------
#include "cabinet.h"
#include "rcids.h"
#include "cabdde.h"
#include "cabwnd.h"
#include "svcs.h"
#include "cabwnd.h"
#include <shellp.h>
#include <shguidp.h>
#include <brfcasep.h>
#include <dbt.h> // for WM_DEVICECHANGE
#include <dde.h>
#include <regstr.h>
// in tray.c
extern BOOL WINAPI Reg_GetString(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPTSTR psz, DWORD cb);
extern UINT g_msgMSWheel;
#undef ILIsEqual
COMPAREROOT *g_psCR = NULL;
HWND v_hwndDesktop = NULL;
HWND g_hwndDDEML = NULL;
HWND g_hwndClient = NULL;
ULONG s_uDtFSRegisterID = 0;
HDPA g_hdpaRoots = NULL;
#define RECTWIDTH(rc) ((rc).right-(rc).left)
#define RECTHEIGHT(rc) ((rc).bottom-(rc).top)
BOOL Tray_IsAutoHide();
void Cabinet_InitGlobalMetrics(WPARAM, LPTSTR);
void FileCabinet_GetViewRect(PFileCabinet this, RECT * prc);
void AppBarSubtractRects(LPRECT lprc);
void DoExitWindows(HWND hwnd);
void UnregisterGlobalHotkeys();
void HandleGlobalHotkey();
void BullyIconsOnScreen(PFileCabinet pfc, int iWidth, int iHeight);
BOOL IsHwndObscured(HWND hwnd);
#define DM_DDETRACE DM_TRACE
#define IDT_DDETIMEOUT 1
#define DTT_DISKFULL 2
//// RESERVE from DTT_DISKFULL to DTT_DISKFULL + 26 to specify which disk is full
#define DISKFULL_TIMEOUT (3 * 1000) // 3 seconds
#define IDT_DELAYQUIT 30
#define DELAYQUIT_TIMEOUT (15 * 1000) // 15 seconds (get this from registry?)
// File system notifyication
#define WM_DT_FSNOTIFY (WM_USER+42)
#ifdef WINNT
// BUGBUG LATER: This is only a temporary solution for NT. We can have a much more
// efficient implementation inside USER on both NT and Win9x. Then we could have
// common NT/Win9x code that says "if OnActiveDesktop() ..." below in Desktop_OnFSNotify
BOOL IsMyDesktopActive(){
BOOL bResult = FALSE;
HDESK hdeskInput;
HDESK hdeskMyDesktop;
TCHAR szInput[256];
TCHAR szMyDesktop[256];
DWORD cb;
hdeskInput = OpenInputDesktop(0,
FALSE,
STANDARD_RIGHTS_REQUIRED |
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hdeskInput != NULL) {
if (GetUserObjectInformation(hdeskInput, UOI_NAME, (PVOID)szInput, sizeof(szInput), &cb)) {
hdeskMyDesktop = GetThreadDesktop(GetCurrentThreadId());
if (hdeskMyDesktop != NULL ) {
if (GetUserObjectInformation(hdeskMyDesktop, UOI_NAME, (PVOID)szMyDesktop, sizeof(szMyDesktop), &cb)) {
bResult = lstrcmpi(szInput, szMyDesktop) == 0;
}
}
}
CloseDesktop(hdeskInput);
}
return bResult;
}
#endif
//---------------------------------------------------------------------------
STDMETHODIMP ShellDesktop_SetToolbarItems(IShellBrowser *psb, LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags)
{
return(E_NOTIMPL);
}
STDMETHODIMP ShellDesktop_BrowseObject(LPSHELLBROWSER psb, LPCITEMIDLIST pdil, UINT wFlags)
{
// REVIEW: Don't we need to implement this?
return E_FAIL;
}
STDMETHODIMP ShellDesktop_GetControlWindow(LPSHELLBROWSER psb, UINT id, HWND * lphwnd)
{
// No need to support this.
return E_FAIL;
}
STDMETHODIMP ShellDesktop_SendControlMsg(LPSHELLBROWSER psb, UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT * pret)
{
// We never need to implement this.
return E_FAIL;
}
//---------------------------------------------------------------------------
STDMETHODIMP ShellDesktop_OnViewWindowActive(LPSHELLBROWSER psb, LPSHELLVIEW psv)
{
return NOERROR;
}
STDMETHODIMP ShellDesktop_ContextSensitiveHelp(LPSHELLBROWSER psb, BOOL fEnable)
{
// BUGBUG: Implement it later!
return E_NOTIMPL;
}
STDMETHODIMP ShellDesktop_SetStatusText(LPSHELLBROWSER psb, LPCOLESTR pwch)
{
// We don't have status bar.
return NOERROR;
}
STDMETHODIMP ShellDesktop_InsertMenus(LPSHELLBROWSER psb, HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
// We don't have menu.
return NOERROR;
}
STDMETHODIMP ShellDesktop_SetMenu(LPSHELLBROWSER psb, HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
{
// We don't have menu.
return NOERROR;
}
STDMETHODIMP ShellDesktop_RemoveMenus(LPSHELLBROWSER psb, HMENU hmenuShared)
{
// We don't have menu.
return NOERROR;
}
// desktop IShellBrowser vtbl
IShellBrowserVtbl s_DesktopSBVtbl =
{
// *** IUnknown methods ***
CFileCabinet_QueryInterface,
CFileCabinet_AddRef,
CFileCabinet_Release,
// *** IOleWindow methods ***
CFileCabinet_GetWindow,
ShellDesktop_ContextSensitiveHelp,
// *** IShellBrowser methods ***
ShellDesktop_InsertMenus,
ShellDesktop_SetMenu,
ShellDesktop_RemoveMenus,
ShellDesktop_SetStatusText,
CFileCabinet_EnableModeless,
CFileCabinet_TranslateAccelerator,
ShellDesktop_BrowseObject,
CDeskTray_GetViewStateStream,
ShellDesktop_GetControlWindow,
ShellDesktop_SendControlMsg,
CFileCabinet_QueryActiveShellView,
ShellDesktop_OnViewWindowActive,
ShellDesktop_SetToolbarItems,
};
PFileCabinet CreateSimpleFileCabinet(HWND hwnd, IShellBrowserVtbl * pvtbl)
{
PFileCabinet pfc = (PFileCabinet)LocalAlloc(LPTR, SIZEOF(CFileCabinet));
if (pfc)
{
pfc->sb.lpVtbl = pvtbl; // const->non const
pfc->cRef = 1;
pfc->hwndMain = hwnd;
//pfc->hMenu = NULL;
//pfc->hwndView = NULL;
//pfc->wLastParam = 0;
//pfc->lLastParam = 0;
//pfc->lpndOpen = NULL;
Assert(pfc->uFocus == FOCUS_VIEW);
}
return pfc;
}
//---------------------------------------------------------------------------
// Create desktop IShellView instance
HWND Desktop_CreateShellView(PFileCabinet pfc, LPFOLDERSETTINGS lpfs)
{
LPSHELLFOLDER psf;
TCHAR szYouLoose[256];
if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellDesktop, &IID_IShellFolder, &psf)))
{
IShellView *psvDesktop;
HRESULT hres = psf->lpVtbl->CreateViewObject(psf, pfc->hwndMain, &IID_IShellView, &psvDesktop);
psf->lpVtbl->Release(psf);
if (SUCCEEDED(hres))
{
RECT rcView;
TCHAR szPath[MAX_PATH];
pfc->pidl = SHCloneSpecialIDList(pfc->hwndMain, CSIDL_DESKTOPDIRECTORY, FALSE);
pfc->psv = psvDesktop;
FileCabinet_GetViewRect(pfc, &rcView);
if (FAILED(psvDesktop->lpVtbl->CreateViewWindow(psvDesktop, NULL, lpfs, &pfc->sb, &rcView, &pfc->hwndView)))
{
pfc->hwndView = NULL;
}
// set the current directory to be the desktop.
// do it because it's better than some random other directory that
// the user had (it's a good logical place to start for the tray run browse dialog)
// and we have the pidl here...
SHGetPathFromIDList(pfc->pidl, szPath);
SetCurrentDirectory(szPath);
return pfc->hwndView;
}
}
// BUGBUG (beta-only): deal with the inability to create the desktop
LoadString(hinstCabinet, IDS_YOULOSE, szYouLoose, ARRAYSIZE(szYouLoose));
MessageBox(NULL, szYouLoose, NULL, MB_ICONSTOP);
return NULL;
}
//---------------------------------------------------------------------------
// Handle creation of a new Desktop folder window. Creates everything except
// the viewer part.
// Returns -1 if something goes wrong.
LRESULT Desktop_OnCreate(HWND hWnd, LPCREATESTRUCT lpcs)
{
PFileCabinet pfc = CreateSimpleFileCabinet(hWnd, &s_DesktopSBVtbl);
if (pfc)
{
PCABVIEW pcv = lpcs->lpCreateParams;
SetPFC(hWnd, pfc);
pfc->hMainAccel = LoadAccelerators(hinstCabinet, MAKEINTRESOURCE(ACCEL_DESKTOP));
if (Desktop_CreateShellView(pfc, &pcv->fs)) {
BullyIconsOnScreen(pfc, g_cxWorkArea, g_cyWorkArea);
return 1; // success
}
}
return (LRESULT)-1; // failure
}
LRESULT Desktop_OnNotify(PFileCabinet pfc, LPNMHDR lpNmhdr)
{
switch (lpNmhdr->code) {
case SEN_DDEEXECUTE:
return(DDEHandleViewFolderNotify(pfc, (LPNMVIEWFOLDER)lpNmhdr));
case NM_STARTWAIT:
case NM_ENDWAIT:
Cabinet_OnWaitCursorNotify(pfc, lpNmhdr);
break;
}
return 0L;
}
// HACKHACK: this hard codes in that we know a listview is the child
// of the view.
HWND HackGetDesktopListview(PFileCabinet pfcDesktop)
{
HWND hwndList = NULL;
if (!pfcDesktop && v_hwndDesktop)
pfcDesktop = GetPFC(v_hwndDesktop);
if (pfcDesktop && ((hwndList = FindWindowEx(pfcDesktop->hwndView, NULL,
c_szListViewClass, NULL)) == NULL))
{
Assert(FALSE);
}
return hwndList;
}
void BullyIconsOnScreen(PFileCabinet pfc, int iWidth, int iHeight)
{
HWND hwndList;
int i;
POINT pt;
DWORD dwStyle;
BOOL bArrangeAfter = FALSE;
if ((hwndList = HackGetDesktopListview(pfc)) == NULL)
return;
// If Auto-Arrange is on, bail... it will do the work for us
dwStyle = GetWindowStyle(hwndList);
Assert((dwStyle & LVS_TYPEMASK) == LVS_ICON);
if (dwStyle & LVS_AUTOARRANGE)
{
return;
}
SetWindowRedraw(hwndList, FALSE);
// Move everything.
for (i = ListView_GetItemCount(hwndList) - 1; i >= 0; i--) {
POINT ptNew;
ListView_GetItemPosition(hwndList, i, &pt);
if (pt.x < (-g_cxIcon/2)) {
ptNew.x = 0;
} else if (pt.x > (iWidth - (g_cxIcon/2))) {
ptNew.x = iWidth - g_cxIcon;
} else
ptNew.x = pt.x;
if (pt.y < (-g_cyIcon/2)) {
ptNew.y = 0;
} else if (pt.y > (iHeight - (g_cyIcon/2))) {
ptNew.y = iHeight - g_cyIcon;
} else
ptNew.y = pt.y;
if ((ptNew.x != pt.x) || (ptNew.y != pt.y))
{
LV_HITTESTINFO hti;
hti.pt.x = ptNew.x;
hti.pt.y = ptNew.y;
hti.flags = LVHT_ONITEM;
if ( -1 != ListView_HitTest( hwndList, &hti ) )
{
bArrangeAfter = TRUE; // doh! We hit another item, therefore resolve later!
}
ListView_SetItemPosition(hwndList, i, ptNew.x, ptNew.y);
}
}
/* If we hit anything whilst moving the items around then ensure that we snap to
/ grid, this will resolve overlapping items by moving them to nice boundaries */
if ( bArrangeAfter )
{
ListView_Arrange( hwndList, LVA_SNAPTOGRID );
}
SetWindowRedraw(hwndList, TRUE);
}
#if defined(WINNT) || defined(MEMPHIS)
#ifndef ENUM_REGISTRY_SETTINGS
#define ENUM_REGISTRY_SETTINGS ((DWORD)-2)
#endif
BOOL IsTempDisplayMode()
{
DEVMODE dm;
BOOL fTempMode = FALSE;
ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(dm);
if (EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &dm) &&
dm.dmPelsWidth > 0 && dm.dmPelsHeight > 0)
{
HDC hdc = GetDC(NULL);
int xres = GetDeviceCaps(hdc, HORZRES);
int yres = GetDeviceCaps(hdc, VERTRES);
ReleaseDC(NULL, hdc);
if (xres != (int)dm.dmPelsWidth || yres != (int)dm.dmPelsHeight)
fTempMode = TRUE;
}
return fTempMode;
}
#else // WIN95
static TCHAR const c_szDisplaySettings[] = REGSTR_PATH_DISPLAYSETTINGS;
static TCHAR const c_szDisplayRes[] = REGSTR_VAL_RESOLUTION;
BOOL IsTempDisplayMode()
{
char ach[80];
BOOL fTempMode = FALSE;
if (Reg_GetString(HKEY_CURRENT_CONFIG, c_szDisplaySettings, c_szDisplayRes, ach, SIZEOF(ach)))
{
int xres = StrToInt(ach);
HDC hdc = GetDC(NULL);
if ((GetDeviceCaps(hdc, CAPS1) & C1_REINIT_ABLE) &&
(xres > 0) && (xres != GetDeviceCaps(hdc, HORZRES))) {
fTempMode = TRUE;
}
ReleaseDC(NULL, hdc);
}
return fTempMode;
}
#endif
void SetViewArea(PFileCabinet pfc, LPRECT lprcNew)
{
RECT rcWindow;
int iWidth, iHeight;
GetWindowRect(pfc->hwndView, &rcWindow);
if (rcWindow.left != lprcNew->left ||
rcWindow.right != lprcNew->right ||
rcWindow.top != lprcNew->top ||
rcWindow.bottom != lprcNew->bottom
) {
BOOL fBullyIcons=TRUE;
TCHAR ach[20];
// erase it all so the wallpaper is repainted correctly
InvalidateRect(pfc->hwndView, NULL, TRUE);
iWidth = RECTWIDTH(*lprcNew);
iHeight = RECTHEIGHT(*lprcNew);
SetWindowPos(pfc->hwndView, NULL, lprcNew->left, lprcNew->top,
iWidth, iHeight,
SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE);
//
// dont bully the icons on the screen, if this is a temorary
// screen change. we can tell if the screen change is temporary
// if the current screen size is not equal to the size in the
// registry.
//
if (IsTempDisplayMode())
{
fBullyIcons = FALSE;
}
if (fBullyIcons)
BullyIconsOnScreen(pfc, iWidth, iHeight);
}
}
// we get called here when new drives come and go;
// things like net connections, hot insertions, etc.
//---------------------------------------------------------------------------
void Desktop_OnDeviceBroadcast(PFileCabinet pfc, UINT code, DEV_BROADCAST_HDR *pbh)
{
int nDrive;
TCHAR szPath[4];
DWORD dwDrives;
DWORD notify;
BOOL fAdd=TRUE;
LPITEMIDLIST pidl;
// do a bunch of this stuff here in desktop so it only happens
// once...
switch (code)
{
#ifndef DBT_NO_DISK_SPACE
#define DBT_NODISKSPACE DBT_NO_DISK_SPACE
#endif
case DBT_NO_DISK_SPACE:
SetTimer(v_hwndDesktop, DTT_DISKFULL + (LPARAM)pbh, DISKFULL_TIMEOUT, NULL);
break;
case DBT_DEVICEREMOVECOMPLETE: // drive or media went away
fAdd = FALSE;
// fall through...
case DBT_DEVICEARRIVAL: // new drive or media (or UNC) has arrived
// Don't process if we are being shutdown...
if (!IsWindowVisible(pfc->hwndMain))
break;
switch (pbh->dbch_devicetype) {
case DBT_DEVTYP_NET: // it is a UNC name comming
// Tell the hood to update as things have probably changed!
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_NETHOOD, &pidl)))
{
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidl, pidl);
SHFree(pidl);
}
#define pbn ((DEV_BROADCAST_NET *)pbh)
// use UNCToNetID(loop if (pbn->dbcn_resource)
break;
case DBT_DEVTYP_VOLUME: // it is a drive
#define pbv ((DEV_BROADCAST_VOLUME *)pbh)
// Get list of still valid drives that is in the mask returned...
//BUGBUG?? dwDrives = GetLogicalDrives() & pbv->dbcv_unitmask;
dwDrives = pbv->dbcv_unitmask;
if (pbv->dbcv_flags & DBTF_MEDIA)
notify = fAdd ? SHCNE_MEDIAINSERTED : SHCNE_MEDIAREMOVED;
else
notify = fAdd ? SHCNE_DRIVEADD : SHCNE_DRIVEREMOVED;
for (nDrive = 0; nDrive < 26; nDrive++) {
if ((1 << nDrive) & dwDrives) {
InvalidateDriveType(nDrive); // must reset...
PathBuildRoot(szPath, nDrive);
SHChangeNotify(notify, SHCNF_PATH, szPath, NULL);
// Generate a SHCNE_DRIVEADDGUI notify for all
// new drives except network ones. These will
// be generated externally...
if (fAdd && !(pbv->dbcv_flags & DBTF_NET))
{
// If the DBTF_MEDIA is not set, do not send
// this notification for CDROM or Floppy as
// they may have come from a new device and not
// have any media in them...
UINT uDriveType = DRIVE_UNKNOWN; // Something other than CDROM...
if (!(pbv->dbcv_flags & DBTF_MEDIA))
uDriveType = DriveType(nDrive);
if ((uDriveType != DRIVE_CDROM) &&
(uDriveType != DRIVE_REMOVABLE))
{
SHChangeNotify(SHCNE_DRIVEADDGUI, SHCNF_PATH, szPath, NULL);
}
}
}
}
// for the non net case force these through right away to make those
// cd-rom autorun things come up faster
if (!(pbv->dbcv_flags & DBTF_NET))
SHChangeNotify(0, SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, NULL, NULL);
break;
}
break;
#ifdef SYNC_BRIEFCASE
case DBT_CONFIGCHANGED:
Desktop_UpdateBriefcaseOnEvent(pfc->hwndMain, UOE_CONFIGCHANGED);
break;
case DBT_QUERYCHANGECONFIG:
Desktop_UpdateBriefcaseOnEvent(pfc->hwndMain, UOE_QUERYCHANGECONFIG);
break;
#endif // SYNC_BRIEFCASE
}
}
//---------------------------------------------------------------------------
BOOL AppAllowsAutoRun(HWND hwndApp)
{
static UINT uMessage = 0;
DWORD dwCancel = 0;
if (!uMessage)
uMessage = RegisterWindowMessage(TEXT("QueryCancelAutoPlay"));
SendMessageTimeout(hwndApp, uMessage, 0, 0, SMTO_NORMAL | SMTO_ABORTIFHUNG,
1000, &dwCancel);
return (dwCancel != 1); // check for exactly 1 (future expansion)
}
//---------------------------------------------------------------------------
void Desktop_OnFSNotify(LONG lNotification, LPITEMIDLIST* lplpidl)
{
// Currently only handle SHCNE_DRIVEADDGUI...
if (lNotification == SHCNE_DRIVEADDGUI)
{
HWND hwnd = GetForegroundWindow();
BOOL fShellForeground = FALSE;
TCHAR szDrive[80];
DWORD dwRestricted;
// dont run anything if the SHIFT key is down
if (GetAsyncKeyState(VK_SHIFT) < 0)
{
DebugMsg(DM_TRACE, TEXT("Cabinet: SHIFT key is down skipping AutoOpen"));
return;
}
if (hwnd)
fShellForeground = (hwnd == v_hwndTray ||
hwnd == v_hwndDesktop || Cabinet_IsFolderWindow(hwnd));
SHGetPathFromIDList(*lplpidl, szDrive);
CharUpper(szDrive);
Assert(szDrive[1] == TEXT(':'));
// Now make sure that this drive is not restricted!
// Handles cases where drives are mapped in under the covers
// like when a drivespace floppy is discovered.
//
dwRestricted = SHRestricted(REST_NODRIVES);
if (dwRestricted)
{
if ((1 << (szDrive[0]-TEXT('A')) ) & dwRestricted)
{
// Restricted drive...
DebugMsg(DM_TRACE, TEXT("Desktop_OnFSNotify: Restricted Drive(%c)"),
szDrive[0]);
return;
}
}
#ifdef WINNT
// BUGBUG LATER: This is only a temporary solution for NT. We can have a much more
// efficient implementation inside USER on both NT and Win9x. Then we could have
// common NT/Win9x code that says "if OnActiveDesktop() ..." here.
// On NT, bail out now if we're on the secure desktop (locked
// workstation or password-protected screensaver)
if (IsMyDesktopActive() == FALSE)
{
return;
}
#endif
if ((DriveIsAutoOpen(szDrive[0]-TEXT('A')) ||
(fShellForeground && DriveIsShellOpen(szDrive[0]-TEXT('A')))) &&
(!DriveIsAutoRun(szDrive[0]-TEXT('A')) || AppAllowsAutoRun(hwnd)))
{
//
// use ShellExecuteEx() so the default verb gets invoked
// (may not be open or even explore)
//
SHELLEXECUTEINFO ei = {
SIZEOF(ei), // size
SEE_MASK_INVOKEIDLIST, // flags
v_hwndDesktop, // parent window
NULL, // verb
NULL, // file
szDrive, // params
szDrive, // directory
SW_NORMAL, // show.
NULL, // hinstance
*lplpidl, // IDLIST
NULL, // class name
NULL, // class key
0, // hot key
NULL, // icon
NULL, // hProcess
};
BOOL fPrevMode = g_CabState.fNewWindowMode;
g_CabState.fNewWindowMode = TRUE;
if (!ShellExecuteEx(&ei))
{
DebugMsg(DM_TRACE, TEXT("Cabinet: ShellExecuteEx() failed on drive notify"));
}
g_CabState.fNewWindowMode = fPrevMode;
}
}
}
//---------------------------------------------------------------------------
LRESULT _ForwardDDEMsgs(HWND hwnd, HWND hwndForward, UINT uMsg,
WPARAM wParam, LPARAM lParam, BOOL fSend)
{
DebugMsg(DM_DDETRACE, TEXT("c.fdm: Forwarding DDE to %x"), hwndForward);
if (hwndForward && IsWindow(hwndForward))
{
DebugMsg(DM_DDETRACE, TEXT("c.fdm: %lx %lx %lx"), uMsg, (WPARAM)hwnd, lParam);
if (fSend)
return SendMessage(hwndForward, uMsg, (WPARAM)hwnd, lParam);
else
return PostMessage(hwndForward, uMsg, (WPARAM)hwnd, lParam);
}
else
{
DebugMsg(DM_DDETRACE, TEXT("c.fdm: Invalid DDEML window, Can't forward DDE messages."));
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
//---------------------------------------------------------------------------
// Set/cleared by dde connect/disconnect.
extern HWND g_hwndDde;
static DWORD g_dwAppFlags = DDECONV_NONE;
const TCHAR c_szExplorerTopic[] = TEXT("Explorer");
// This is the 16-bit/Win95 window class name
static TCHAR const c_szDMGFrame[] = TEXT("DMGFrame");
#ifdef WINNT
// this is the 32-bit NT window class name
static TCHAR const c_szDDEMLMom[] = TEXT("DDEMLMom");
#endif
//---------------------------------------------------------------------------
// Broadcast to all ddeml server windows.
void DDEML_Broadcast(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
TCHAR szClass[32];
HWND hwnd;
hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
while (hwnd)
{
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
if (lstrcmp(szClass, c_szDMGFrame) == 0)
SendMessage(hwnd, uMsg, wParam, lParam);
#ifdef WINNT
if (lstrcmp(szClass, c_szDDEMLMom) == 0)
SendMessage(hwnd, uMsg, wParam, lParam);
#endif
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
}
}
//---------------------------------------------------------------------------
LRESULT _HandleDDEInitiateAndAck(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static BOOL g_fInInit = FALSE;
ATOM aProgman;
TCHAR szService[32];
TCHAR szTopic[32];
TCHAR szClass[32];
UINT uHigh, uLow;
BOOL fForceAccept = FALSE;
if (uMsg == WM_DDE_INITIATE)
{
DebugMsg(DM_DDETRACE, TEXT("c.hdi: Init."));
// Don't handle DDE messages if we're already using DDEML. This happens when apps
// broadcast DDE_INIT and don't stop on the first reply. Both our DDEML window and
// the desktop end up replying. Most apps don't care and just talk to the first or
// the last one but Ventura gets confused and thinks it's finished doing DDE when it
// gets the second ACK and destroys it's internal DDE window.
if (g_hwndDde)
{
DebugMsg(DM_DDETRACE, TEXT("c.fpwp: Not forwarding DDE, DDEML is handing it."));
KillTimer(hwnd, IDT_DDETIMEOUT);
}
// Are we re-cursing?
else if (!g_fInInit)
{
// Nope, Is this for Progman, Progman or Shell, AppProperties?
if (lParam)
{
GlobalGetAtomName(LOWORD(lParam), szService, ARRAYSIZE(szService));
GlobalGetAtomName(HIWORD(lParam), szTopic, ARRAYSIZE(szTopic));
}
else
{
// Progman allowed a null Service & a null Topic to imply Progman, Progman.
szService[0] = TEXT('\0');
szTopic[0] = TEXT('\0');
fForceAccept = TRUE;
}
// Keep track of hacks, we reset this on the disconnect.
g_dwAppFlags = GetDDEAppFlagsFromWindow((HWND)wParam);
// Hacks for WinFax and Journeyman Project.
if ((g_dwAppFlags & DDECONV_EXPLORER_SERVICE_AND_TOPIC)
&& (lstrcmpi(szTopic, c_szExplorerTopic) == 0)
&& (lstrcmpi(szService, c_szExplorerTopic) == 0))
{
fForceAccept = TRUE;
}
if (((lstrcmpi(szTopic, c_szTopic) == 0) && (lstrcmpi(szService, c_szService) == 0)) ||
fForceAccept)
{
DebugMsg(DM_DDETRACE, TEXT("c.hdi: Init on [Progman,Progman] - needs forwarding."));
// Nope go find it.
// NB This will cause an echo on every DDE_INIT for Progman, Progman after booting.
// It shouldn't be a problem :-)
// Keep track of who to send Acks back to.
g_hwndClient = (HWND)wParam;
// Now find the real shell.
aProgman = GlobalAddAtom(c_szService);
DebugMsg(DM_DDETRACE, TEXT("c.d_hdm: Finding shell dde handler..."));
g_fInInit = TRUE;
// SendMessage(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwnd, MAKELPARAM(aProgman, aProgman));
DDEML_Broadcast(WM_DDE_INITIATE, (WPARAM)hwnd, MAKELPARAM(aProgman, aProgman));
g_fInInit = FALSE;
DebugMsg(DM_DDETRACE, TEXT("c.d_hdm: ...Done"));
GlobalDeleteAtom(aProgman);
}
else
{
DebugMsg(DM_DDETRACE, TEXT("c.hdi: Init on something other than [Progman,Progman] - Ignoring"));
KillTimer(hwnd, IDT_DDETIMEOUT);
}
}
else
{
DebugMsg(DM_DDETRACE, TEXT("c.hdi: Recursing - Init ignored."));
}
return 0;
}
else if (uMsg == WM_DDE_ACK)
{
DebugMsg(DM_DDETRACE, TEXT("c.hdi: Ack."));
// Is this in response to the DDE_Init above?
if (g_fInInit)
{
// Yep, keep track of who we're talking too.
GetClassName((HWND)wParam, szClass, ARRAYSIZE(szClass));
DebugMsg(DM_DDETRACE, TEXT("c.d_hdm: Init-Ack from %x (%s)."), wParam, szClass);
g_hwndDDEML = (HWND)wParam;
// The forward it back (send it, don't post it - Breaks Prodogy).
return _ForwardDDEMsgs(hwnd, g_hwndClient, uMsg, (WPARAM)hwnd, lParam, TRUE);
}
else
{
// Nope, just forward it back.
// Hack for WinFaxPro.
if (g_dwAppFlags & DDECONV_USING_SENDMSG)
{
// We copied the data before sending it on so we can free it here.
// WinFax ignores the reply so don't bother sending it.
UnpackDDElParam(uMsg, lParam, &uLow, &uHigh);
if (uHigh)
GlobalFree((HGLOBAL)uHigh);
return 0;
}
return _ForwardDDEMsgs(hwnd, g_hwndClient, uMsg, (WPARAM)hwnd, lParam, FALSE);
}
}
return 0;
}
//---------------------------------------------------------------------------
LRESULT _HandleDDEForwardBiDi(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if ((HWND)wParam == g_hwndDDEML)
return _ForwardDDEMsgs(hwnd, g_hwndClient, uMsg, wParam, lParam, FALSE);
else if ((HWND)wParam == g_hwndClient)
return _ForwardDDEMsgs(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
else
return 0;
}
//---------------------------------------------------------------------------
LRESULT _HandleDDETerminate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND hwndClient;
DebugMsg(DM_TRACE, TEXT("c.hddet: Terminate."));
if ((HWND)wParam == g_hwndDDEML)
{
// This should be the last message (a terminate from ddeml to the client).
// Cleanup now.
KillTimer(hwnd, IDT_DDETIMEOUT);
DebugMsg(DM_TRACE, TEXT("c.hddet: Cleanup."));
hwndClient = g_hwndClient;
g_hwndClient = NULL;
g_hwndDDEML = NULL;
g_dwAppFlags = DDECONV_NONE;
return _ForwardDDEMsgs(hwnd, hwndClient, uMsg, wParam, lParam, FALSE);
}
else if ((HWND)wParam == g_hwndClient)
{
return _ForwardDDEMsgs(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
}
else
{
return 0;
}
}
void SHGlobalDefect(DWORD dwHnd32);
//---------------------------------------------------------------------------
LRESULT _HandleDDEExecute(HWND hwnd, HWND hwndForward, UINT uMsg,
WPARAM wParam, LPARAM lParam, BOOL fSend)
{
ATOM aApp, aTopic;
HANDLE hNew;
LPTSTR pNew, pOld;
UINT cb;
// NB WinFaxPro does a Send/Free which avoids Users DDE hack
// and means they get to delete the data while we're in
// the middle of using it so we must copy it here. We'll
// clean it up on the Ack.
// NB WinFaxPro re-uses the same 16bit selector for all their
// messages which the thunk layer can't handle it. We need to
// defect the 32bit side (and free it) so the next time they
// send the 16bit handle through the thunk layer they get a
// new 32bit version.
if (g_dwAppFlags & DDECONV_USING_SENDMSG)
{
cb = GlobalSize((HGLOBAL)lParam);
hNew = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb);
if (hNew)
{
// Copy the old data.
pNew = GlobalLock(hNew);
pOld = GlobalLock((HGLOBAL)lParam);
memcpy(pNew, pOld, cb);
GlobalUnlock((HGLOBAL)lParam);
GlobalUnlock(hNew);
// Tell kernel we're done with 32bit side
// of the old handle and then free it.
SHGlobalDefect((DWORD)lParam);
GlobalFree((HGLOBAL)lParam);
// Use our copy.
lParam = (LPARAM)hNew;
}
}
// NB CA neglect to send a DDE_INIT, they just start
// throwing DDE_EXEC's at us so we fake up an init
// from them to DDEML to get things rolling.
if (!hwndForward)
{
if (!(g_dwAppFlags & DDECONV_NO_INIT))
g_dwAppFlags = GetDDEAppFlagsFromWindow((HWND)wParam);
if (g_dwAppFlags & DDECONV_NO_INIT)
{
aApp = GlobalAddAtom(c_szService);
aTopic = GlobalAddAtom(c_szTopic);
SendMessage(hwnd, WM_DDE_INITIATE, wParam, MAKELPARAM(aApp, aTopic));
GlobalDeleteAtom(aApp);
GlobalDeleteAtom(aTopic);
hwndForward = g_hwndDDEML;
}
}
return _ForwardDDEMsgs(hwnd, hwndForward, uMsg, wParam, lParam, fSend);
}
//---------------------------------------------------------------------------
// Stupid hacks to get various apps installed (read: ATM). These are the people
// who do a FindWindow for Progman and then do dde to it directly.
// These people should not be allowed to write code.
LRESULT Desktop_HandleDDEMsgs(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DebugMsg(DM_DDETRACE, TEXT("c.fpwp: Forwarding DDE."));
SetTimer(hwnd, IDT_DDETIMEOUT, 30*1000, NULL);
switch (uMsg)
{
case WM_DDE_INITIATE:
case WM_DDE_ACK:
return _HandleDDEInitiateAndAck(hwnd, uMsg, wParam, lParam);
case WM_DDE_TERMINATE:
return _HandleDDETerminate(hwnd, uMsg, wParam, lParam);
case WM_DDE_DATA:
return _HandleDDEForwardBiDi(hwnd, uMsg, wParam, lParam);
case WM_DDE_ADVISE:
case WM_DDE_UNADVISE:
case WM_DDE_REQUEST:
case WM_DDE_POKE:
return _ForwardDDEMsgs(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
case WM_DDE_EXECUTE:
return _HandleDDEExecute(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
}
return 0;
}
//---------------------------------------------------------------------------
// Some installers (Wep2) forget to Terminate a conversation so we timeout
// after not getting any dde-messages for a while. If we don't, and you run
// a Wep2 install a second time we think the installer is already talking via
// ddeml so we don't reply from the desktop. Wep2 then thinks Progman isn't
// running, does a WinExec of Progman and hangs waiting to talk to it. Progman
// never replies since it is not the shell. Nasty Nasty Nasty.
void _HandleDDETimeout(HWND hwnd)
{
HWND hwndClient, hwndDDEML;
DebugMsg(DM_TRACE, TEXT("c.hdt: DDE Timeout."));
KillTimer(hwnd, IDT_DDETIMEOUT);
// Has everything gone away yet?
if (g_hwndDDEML && g_hwndClient)
{
// Nope. Don't want to forward anymore.
hwndClient = g_hwndClient;
hwndDDEML = g_hwndDDEML;
g_hwndClient = NULL;
g_hwndDDEML = NULL;
g_dwAppFlags = DDECONV_NONE;
// Shutdown our ddeml alter-ego.
// NB If the client window has already gone away (very likely) it's not a
// problem, ddeml will skip posting the reply but will still do the
// disconnect callback.
PostMessage(hwndDDEML, WM_DDE_TERMINATE, (WPARAM)hwnd, 0);
}
}
CR_RETURN Desktop_CompareRoot(HANDLE hCR, DWORD dwProcId)
{
COMPAREROOT *psCR = NULL;
CR_MASK maskThis;
CR_MASK maskThat;
CR_RETURN crReturn = CR_DIFFERENT;
if (hCR)
{
psCR = (COMPAREROOT *)SHLockShared(hCR, dwProcId);
if (!psCR)
{
return(CR_DIFFERENT);
}
}
maskThis = g_psCR ? g_psCR->mask : 0;
maskThat = psCR ? psCR->mask : 0;
// Must have the same mask to be equal
if (((maskThis^maskThat) & (CR_CLSID|CR_IDLROOT)) == 0)
{
crReturn = CR_SAME;
// It's up to the class to handle being called with different roots
if (maskThis&CR_CLSID)
{
if (!IsEqualGUID(&psCR->clsid, &g_psCR->clsid))
crReturn = CR_DIFFERENT;
}
// I should probably compare IDList's so I will recognize an object
// that has several "names", but this really should not be a problem
if (maskThis&CR_IDLROOT)
{
if (!ILIsEqual(&psCR->idlRoot, &g_psCR->idlRoot))
crReturn = CR_DIFFERENT;
}
}
if (psCR)
SHUnlockShared(psCR);
return crReturn;
}
LRESULT Desktop_HandleSpecifyCompare( HANDLE hcr)
{
LPCOMPAREROOT lpcr;
lpcr = SHLockShared(hcr, GetCurrentProcessId());
if (lpcr)
{
if (lpcr->mask == CR_REMOVE)
{
// Deletion case!
FolderList_RemoveCompare(lpcr);
}
else
{
// Specifying a root/folder case
LPCOMPAREROOT lpcrCopy = LocalAlloc(LPTR, lpcr->uSize);
if (lpcrCopy)
{
hmemcpy(lpcrCopy, lpcr, lpcr->uSize);
FolderList_AddCompare(lpcrCopy);
}
}
}
SHUnlockShared(lpcr);
SHFreeShared(hcr,GetCurrentProcessId());
return TRUE;
}
LRESULT Desktop_HandlePerformCompare( DWORD dwProcId, HANDLE hcr )
{
HWND hwndMatch = NULL;
LPCOMPAREROOT lpcr;
CR_RETURN crResult = CR_DIFFERENT;
// Iterate through all of the roots that we have, see if we have a match
if (!g_hdpaRoots)
return (LRESULT)NULL;
lpcr = SHLockShared(hcr, dwProcId);
if (lpcr)
{
if (FolderList_PerformCompare(lpcr))
crResult = CR_SAME;
SHUnlockShared(lpcr);
}
return (LRESULT)crResult;
}
LRESULT Desktop_HandleFSNotify(HANDLE hChangeNotification, DWORD dwProcId)
{
LPSHChangeNotificationLock pshcnl;
pshcnl = SHChangeNotification_Lock(hChangeNotification, dwProcId,NULL,NULL);
if (pshcnl)
{
SHChangeNotifyReceive(pshcnl->pshcn->lEvent,
pshcnl->pshcn->uFlags,
pshcnl->pidlMain,
pshcnl->pidlExtra);
SHChangeNotification_Unlock(pshcnl);
SHFreeShared(hChangeNotification, dwProcId);
}
return TRUE;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK DesktopWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PFileCabinet pfc = GetPFC(hWnd);
switch (uMsg)
{
case WM_CREATE:
return Desktop_OnCreate(hWnd, (LPCREATESTRUCT)lParam);
case WM_DESTROY:
FolderList_UnregisterWindow(v_hwndDesktop);
SHChangeNotifyDeregister(s_uDtFSRegisterID);
UnregisterGlobalHotkeys();
if (pfc) {
DeskTray_DestroyShellView(pfc);
}
v_hwndDesktop = NULL;
if (g_hdpaRoots)
DPA_Destroy(g_hdpaRoots);
break;
case WM_NOTIFY:
return Desktop_OnNotify(pfc, (LPNMHDR)lParam);
case WM_ERASEBKGND:
PaintDesktop((HDC)wParam);
return 1;
case WM_TIMER:
if (wParam >= DTT_DISKFULL && (wParam <= (DTT_DISKFULL + 26))) {
static DWORD s_dwDiskFull = 0;
int idDrive = wParam - DTT_DISKFULL - 1;
KillTimer(hWnd, wParam);
DebugMsg(DM_TRACE, TEXT("Disk %c: is full"), TEXT('a') + idDrive);
// are we already handling it?
if (!((1 << idDrive) & s_dwDiskFull)) {
s_dwDiskFull |= (1 << idDrive);
SHHandleDiskFull(hWnd, idDrive);
s_dwDiskFull &= ~(1 << idDrive);
} else {
DebugMsg(DM_TRACE, TEXT("Punting because we're already dealing"));
}
}
else if (wParam == IDT_DDETIMEOUT) {
_HandleDDETimeout(hWnd);
}
break;
case WM_SHELLNOTIFY:
DebugMsg(DM_TRACE, TEXT("ca TR - DesktopWndProc got WM_SHELLNOTIFY %x,%x"), wParam, lParam);
switch(wParam)
{
#ifdef SHELLNOTIFY_DISKFULL
case SHELLNOTIFY_DISKFULL:
SetTimer(v_hwndDesktop, DTT_DISKFULL + lParam, DISKFULL_TIMEOUT, NULL);
break;
#endif
case SHELLNOTIFY_WALLPAPERCHANGED:
// this is done only to the shell window when someone sets
// the wall paper but doesn't specify to broadcast
if (pfc && pfc->hwndView)
SendMessage(pfc->hwndView, WM_SHELLNOTIFY, wParam, lParam);
break;
case SHELLNOTIFY_OLELOADED:
case SHELLNOTIFY_OLEUNLOADED:
//
// Some process loaded OLE. Let's load it the shell's process
// space and register all the drop targets to OLE.
//
SHLoadOLE(wParam);
break;
}
break;
case WM_PALETTECHANGED:
case WM_QUERYNEWPALETTE:
//
// the desktop window proc will invalidate the shell window
// when a palette change occurs so we dont have to do anything here.
//
// note that this is different from cabwnd, which forwards these
// messages to the view. it will probably have to change if we ever
// do wallpaper on a per-folder basis...
//
break;
case WM_ACTIVATEAPP:
if (pfc->hwndView)
return SendMessage(pfc->hwndView, uMsg, wParam, lParam);
break;
case WM_DEVICECHANGE:
if (pfc->hwndView)
SendMessage(pfc->hwndView, uMsg, wParam, lParam);
Desktop_OnDeviceBroadcast(pfc, wParam, (DEV_BROADCAST_HDR *)lParam);
goto DoDefault;
case WM_DT_FSNOTIFY:
{
LPSHChangeNotificationLock pshcnl;
LPITEMIDLIST *ppidl;
LONG lEvent;
pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
if (pshcnl)
{
Desktop_OnFSNotify(lEvent, ppidl);
SHChangeNotification_Unlock(pshcnl);
}
}
break;
case WM_ACTIVATE:
if (pfc->hwndView)
SendMessage(pfc->hwndView, uMsg, wParam, lParam);
// this is to get our accelerators working right, basically
// we switch the tray and desktop windows in the main
// loop accelerator dispatch
if (g_ppfcDesktopTray)
{
// this is set in MessageLoop() for us
Assert(*g_ppfcDesktopTray);
DebugMsg(DM_TRACE, TEXT("Setting desktop/tray accelerator"));
if (wParam == WA_INACTIVE)
{
if (v_hwndTray)
*g_ppfcDesktopTray = GetPFC(v_hwndTray);
}
else
{
*g_ppfcDesktopTray = GetPFC(v_hwndDesktop);
// activating the desktop, must tell the tray this ourselves
// since this is in our process
if (v_hwndTray)
SendMessage(v_hwndTray, WM_ACTIVATE, WA_INACTIVE, 0);
}
}
if (wParam == WA_INACTIVE)
break;
// else fall through
case WM_SETFOCUS:
if (pfc->hwndView)
SetFocus(pfc->hwndView);
break;
case WM_WINDOWPOSCHANGING:
// we want to be over the entire screen, but our view should be
// the work area
#define ppos ((LPWINDOWPOS)lParam)
ppos->x = 0;
ppos->y = 0;
ppos->cx = g_cxScreen;
ppos->cy = g_cyScreen;
// fall through
case DTM_SIZEDESKTOP:
case WM_SIZE:
if (wParam == SIZE_MINIMIZED)
{
DebugMsg(DM_TRACE, TEXT("c.dwp: Desktop minimized by somebody!"));
// Put it back.
ShowWindow(hWnd, SW_RESTORE);
}
else if (pfc->hwndView && IsWindow(v_hwndTray))
{
RECT rcWindow;
RECT rcWork;
RECT rcTray;
rcWindow.top = rcWindow.left = 0;
rcWindow.right = g_cxScreen;
rcWindow.bottom = g_cyScreen;
if (!Tray_IsAutoHide()) {
GetWindowRect(v_hwndTray, &rcTray);
SubtractRect(&rcWork, &rcWindow, &rcTray);
} else {
rcWork = rcWindow;
}
AppBarSubtractRects(&rcWork);
SetViewArea(pfc, &rcWork);
}
break;
case WM_SYSCOMMAND:
switch (wParam & 0xFFF0) {
// NB Dashboard 1.0 sends a WM_SYSCOMMAND SC_CLOSE to the desktop when it starts up.
// What it was trying to do was to close down any non-shell versions of Progman. The
// proper shell version would just ignore the close. Under Chicago, they think that
// the desktop is Progman and send it the close, so we put up the exit windows dialog!
// Dashboard 2.0 has been fixed to avoid this bogisity.
case SC_CLOSE:
// DoExitWindows(hWnd);
break;
// America alive tries to minimise Progman after installing - they end up minimising
// the desktop on Chicago!
case SC_MINIMIZE:
break;
default:
goto DoDefault;
}
break;
case WM_SETCURSOR:
if (pfc->iWaitCount) {
//DebugMsg(DM_TRACE,"########### SET WAIT CURSOR WM_SETCURSOR %d", pfc->iWaitCount);
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
return TRUE;
} else
goto DoDefault;
case WM_HOTKEY:
HandleGlobalHotkey(wParam);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case FCIDM_REFRESH:
Cabinet_OnCommand(pfc, wParam, lParam);
break;
case FCIDM_FINDFILES:
SHFindFiles(pfc->pidl, NULL);
break;
case IDC_KBSTART:
case FCIDM_NEXTCTL:
if (v_hwndTray)
return SendMessage(v_hwndTray, uMsg, wParam, lParam);
break;
case IDM_CLOSE:
DoExitWindows(hWnd);
break;
}
break;
case WM_CLOSE:
DoExitWindows(hWnd);
return 0;
case WM_DRAWITEM:
case WM_MEASUREITEM:
if (!Cabinet_ForwardViewMsg(pfc, uMsg, wParam, lParam))
goto DoDefault;
break;
case WM_INITMENUPOPUP:
case WM_ENTERMENULOOP:
case WM_EXITMENULOOP:
// let the fsview deal with any popup menus it created
Cabinet_ForwardViewMsg(pfc, uMsg, wParam, lParam);
break;
// Looking at messages to try to capture when the workarea may
// have changed...
case WM_DISPLAYCHANGE:
lParam = 0L;
// fall through
case WM_WININICHANGE:
Cabinet_InitGlobalMetrics(wParam, (LPTSTR)lParam);
SetWindowPos(hWnd, NULL, 0, 0, g_cxScreen, g_cyScreen,
SWP_NOACTIVATE | SWP_NOZORDER);
Cabinet_OnWinIniChange(pfc, wParam, lParam);
break;
case WM_SYSCOLORCHANGE:
Cabinet_PropagateMessage(hWnd, uMsg, wParam, lParam);
break;
// Don't go to default wnd proc for this one...
case WM_INPUTLANGCHANGEREQUEST:
if (wParam)
goto DoDefault;
else
return(LRESULT)0L;
case CWM_GETISHELLBROWSER:
return (LRESULT)&pfc->sb;
case CWM_COMMANDLINE:
{
PNEWFOLDERINFO pnfi;
pnfi = ConvertHNFBLOCKtoNFI((HNFBLOCK)lParam, GetCurrentProcessId());
if (pnfi)
{
Cabinet_OpenFolderPath(pnfi);
Cabinet_CleanUpCommand(pnfi);
}
}
break;
case CWM_COMPAREROOT:
return(Desktop_CompareRoot((HANDLE)lParam, (DWORD)wParam));
case CWM_SPECIFYCOMPARE:
return Desktop_HandleSpecifyCompare((HANDLE)lParam);
case CWM_PERFORMCOMPARE:
return Desktop_HandlePerformCompare((DWORD)wParam,(HANDLE)lParam);
case CWM_FSNOTIFY:
return Desktop_HandleFSNotify((HANDLE)wParam,(DWORD)lParam);
case CWM_CHANGEREGISTRATION:
return (LRESULT)SHChangeRegistrationReceive((HANDLE)wParam, (DWORD)lParam);
case CWM_ADDTORECENT:
QueueAddToRecent((HANDLE)wParam, (DWORD)lParam);
return 0L;
case CWM_WAITOP:
SHWaitOp_Operate((HANDLE)wParam, (DWORD)lParam);
return 0L;
// Handle DDE messages for badly written apps (that assume the shell's
// window is of class Progman and called Program Manager.
case WM_DDE_INITIATE:
case WM_DDE_TERMINATE:
case WM_DDE_ADVISE:
case WM_DDE_UNADVISE:
case WM_DDE_ACK:
case WM_DDE_DATA:
case WM_DDE_REQUEST:
case WM_DDE_POKE:
case WM_DDE_EXECUTE:
return Desktop_HandleDDEMsgs(hWnd, uMsg, wParam, lParam);
default:
// Handle the MSWheel message - send it to the focus window if it isn't us
if (uMsg == g_msgMSWheel) {
HWND hwndT;
POINT pt;
if (hWnd != (hwndT = GetFocus())) {
PostMessage(hwndT, uMsg, wParam, lParam);
return 1;
}
}
DoDefault:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
BOOL InitDesktopClass(HINSTANCE hInst)
{
WNDCLASS wc;
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = DesktopWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = SIZEOF(PFileCabinet);
wc.hInstance = hInst;
wc.hIcon = NULL;
wc.hCursor = GetClassCursor(GetDesktopWindow());
wc.hbrBackground = (HBRUSH)(COLOR_DESKTOP+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = c_szDesktopClass;
return RegisterClass(&wc);
}
// create the desktop window and its shell view
BOOL InitDesktop(HINSTANCE hInst)
{
RECT rc;
CABVIEW cv;
DWORD cbData;
#ifdef USE_DESKTOP_REGSTREAM
LPSTREAM pstm;
#endif
g_hdpaRoots = DPA_Create(0);
if (!g_hdpaRoots)
return FALSE;
GetClientRect(GetDesktopWindow(), &rc);
#ifdef USE_DESKTOP_REGSTREAM
pstm = OpenRegStream(g_hkeyExplorer, c_szCabinetDeskView, NULL, STGM_READ);
if (!Settings_ReadFromStream(pstm, &cv, 0))
cv.fs.fFlags &= ~(FWF_AUTOARRANGE|FWF_BESTFITWINDOW);
if (pstm)
pstm->lpVtbl->Release(pstm);
#else
Settings_ReadFromStream(NULL, &cv, 0);
cbData = SIZEOF(cv.fs);
if (!Reg_GetStruct(g_hkeyExplorer, c_szCabinetDeskView, c_szSettings, &cv.fs, &cbData)) {
cv.fs.fFlags &= ~(FWF_AUTOARRANGE | FWF_BESTFITWINDOW);
}
#endif
// Turn off those features which are not used by the desktop.
// override the defaults
cv.fs.ViewMode = FVM_ICON; // to make sure we restore positions
cv.fs.fFlags |= FWF_DESKTOP; // make it transparent, etc.
// NB This windows class is Progman and it's title is Program Manager. This makes
// sure apps (like ATM) think that program is running and don't fail their install.
v_hwndDesktop = CreateWindowEx(WS_EX_TOOLWINDOW, c_szDesktopClass, c_szProgman,
WS_POPUP | WS_CLIPCHILDREN, 0, 0, rc.right, rc.bottom, NULL, NULL, hInst, &cv);
if (v_hwndDesktop) {
SHChangeNotifyEntry fsne = { NULL, TRUE }; // Global & recursive.
s_uDtFSRegisterID = SHChangeNotifyRegister(v_hwndDesktop,
SHCNRF_NewDelivery | SHCNRF_ShellLevel,
SHCNE_DRIVEADDGUI, WM_DT_FSNOTIFY, 1, &fsne);
#ifdef WINNT
// This is for repainting the background quickly on NT
{
HWND hwndView;
PFileCabinet pfc = GetPFC(v_hwndDesktop);
if (pfc)
hwndView = GetWindow(pfc->hwndView, GW_CHILD);
else
hwndView = NULL;
SetShellWindowEx(v_hwndDesktop, hwndView);
}
#else
SetShellWindow(v_hwndDesktop);
FolderList_RegisterWindow(v_hwndDesktop,NULL);
#endif
#ifndef BUGBUG_BOBDAY
// On NT, we run the thread that handles Ctrl-Esc with a high priority
// class so that it can respond even on a stressed system. Can this
// be done for win96?
#ifdef WINNT
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);
#endif
#endif
#ifdef WINNT
PostMessage(v_hwndDesktop, WM_SHELLNOTIFY, SHELLNOTIFY_OLELOADED, 0);
#endif
return TRUE;
} else
return FALSE;
}
BOOL CreateDesktopWindows(HINSTANCE hInstance)
{
if (!v_hwndDesktop) {
if (!InitDesktop(hInstance))
goto Error;
}
if (!v_hwndTray) {
if (!InitTray(hInstance))
goto Error;
}
// do this here to avoid painting the desktop, then repainting
// when the tray appears and causes everything to move
if (!g_fNoDesktop)
{
ShowWindow(v_hwndDesktop, SW_SHOW);
UpdateWindow(v_hwndDesktop);
}
else
{
DebugMsg(DM_TRACE, TEXT("c.cdw: Hiding desktop."));
}
return TRUE;
Error:
if (v_hwndTray) {
DestroyWindow(v_hwndTray);
}
if (v_hwndDesktop) {
DestroyWindow(v_hwndDesktop);
}
return FALSE;
}
int g_cInstRef = 0; // The number of secondary threads
//=============================================================================
// CFakeDropTarget : member
//=============================================================================
STDMETHODIMP CFakeDropTarget_QueryInterface(LPDROPTARGET pdtgt, REFIID riid, LPVOID * ppvObj)
{
if (IsEqualIID(riid, &IID_IDropTarget) || IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = pdtgt;
return S_OK;
}
*ppvObj = NULL;
return (E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CFakeDropTarget_AddRef(LPDROPTARGET pdtgt)
{
return 2;
}
STDMETHODIMP_(ULONG) CFakeDropTarget_Release(LPDROPTARGET pdtgt)
{
return 1;
}
STDMETHODIMP CFakeDropTarget_DragEnter(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
return S_OK;
}
STDMETHODIMP CFakeDropTarget_DragOver(LPDROPTARGET pdtgt, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
return S_OK;
}
STDMETHODIMP CFakeDropTarget_DragLeave(LPDROPTARGET pdtgt)
{
return S_OK;
}
STDMETHODIMP CFakeDropTarget_Drop(LPDROPTARGET pdtgt, LPDATAOBJECT pdtobj,
DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
return S_OK;
}
IDropTargetVtbl c_CFakeDropTargetVtbl = {
CFakeDropTarget_QueryInterface,
CFakeDropTarget_AddRef,
CFakeDropTarget_Release,
CFakeDropTarget_DragEnter,
CFakeDropTarget_DragOver,
CFakeDropTarget_DragLeave,
CFakeDropTarget_Drop
};
IDropTarget c_dtgtFake = { &c_CFakeDropTargetVtbl };
//---------------------------------------------------------------------------
// This proxy desktop window procedure is used when we are run and we
// are not the shell. We are a hidden window which will simply respond
// to messages like the ones that create threads for folder windows.
// This window procedure will close after all of the open windows
// associated with it go away.
LRESULT CALLBACK ProxyDesktopWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PFileCabinet pfc = GetPFC(hWnd);
switch (uMsg)
{
case WM_CREATE:
{
pfc = CreateSimpleFileCabinet(hWnd, &s_DesktopSBVtbl);
// InitialiseDDE(FALSE);
SetPFC(hWnd, pfc);
//
// BUGBUG - BobDay - OLE32 does some sort of initialization for
// drop targets. And when you have no drop targets, it gets
// cleaned up. Adding a fake drop target here, allows the 2nd
// folder window to not have to re-initialize that stuff.
// I measured a huge speed up with this little fakery below.
//
if (g_fRunSeparateDesktop)
{
// Need to keep OLE from uninitializing itself
// and slowing us down!
SHRegisterDragDrop(hWnd, &c_dtgtFake);
}
}
break;
#ifdef WINNT
case WM_SHELLNOTIFY:
DebugMsg(DM_TRACE, TEXT("ca TR - ProxyDesktopWndProc got WM_SHELLNOTIFY %x,%x"), wParam, lParam);
switch(wParam)
{
case SHELLNOTIFY_OLELOADED:
case SHELLNOTIFY_OLEUNLOADED:
//
// Some process loaded OLE. Let's load it the shell's process
// space and register all the drop targets to OLE.
//
SHLoadOLE(wParam);
break;
}
break;
#endif
case WM_DESTROY:
if (g_fRunSeparateDesktop)
SHRevokeDragDrop(hWnd);
FolderList_UnregisterWindow(v_hwndDesktop);
v_hwndDesktop = NULL;
if (g_psCR)
{
Free(g_psCR);
g_psCR = NULL;
}
if (g_hdpaRoots)
DPA_Destroy(g_hdpaRoots);
break;
case WM_DEVICECHANGE:
Desktop_OnDeviceBroadcast(pfc, wParam, (DEV_BROADCAST_HDR *)lParam);
goto DoDefault;
case CWM_COMMANDLINE:
{
PNEWFOLDERINFO pnfi;
pnfi = ConvertHNFBLOCKtoNFI((HNFBLOCK)lParam, GetCurrentProcessId());
if (pnfi)
{
KillTimer(v_hwndDesktop, IDT_DELAYQUIT);
Cabinet_OpenFolderPath(pnfi);
Cabinet_CleanUpCommand(pnfi);
}
}
break;
case CWM_COMPAREROOT:
return(Desktop_CompareRoot((HANDLE)lParam, (DWORD)wParam));
case CWM_SPECIFYCOMPARE: // Nobody should post us this message!
case CWM_PERFORMCOMPARE: // Nobody should post us this message!
Assert(FALSE);
break;
case WM_TIMER:
if (wParam == IDT_DELAYQUIT)
{
PostQuitMessage(0);
}
break;
case DTM_THREADEXIT:
// One of our main threads has exited. See if it looks like we can
// bail. If so we post a quit message to ourself...
if (g_cInstRef == 0)
{
if (g_fRunSeparateDesktop && g_fRunNoUI)
{
if (!g_fRunSeparateStartAndStay)
SetTimer(v_hwndDesktop, IDT_DELAYQUIT, DELAYQUIT_TIMEOUT, NULL);
}
else
{
PostQuitMessage(0); // Just quit now
}
}
break;
default:
DoDefault:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
BOOL CreateProxyDesktop(HINSTANCE hInst, const CLSID *pclsid, LPCITEMIDLIST pidlRoot)
{
WNDCLASS wc;
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = ProxyDesktopWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = SIZEOF(PFileCabinet); // Probably need for compatability with real one
wc.hInstance = hInst;
wc.hIcon = NULL;
wc.hCursor = GetClassCursor(GetDesktopWindow());
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = c_szProxyDesktopClass;
if (!RegisterClass(&wc))
return(FALSE);
if (pclsid || pidlRoot)
{
UINT uLen = pidlRoot ? ILGetSize(pidlRoot) : 0;
// I am allocating this global so it can be used in SendMessage to other
// instances later in life
g_psCR = Alloc(SIZEOF(COMPAREROOT) + uLen);
if (!g_psCR)
{
// If this fails, something else will soon enough
return(FALSE);
}
g_psCR->uSize = uLen;
g_psCR->hwnd = (HWND)NULL; // Filled in later
g_psCR->mask = 0;
if (pclsid)
{
g_psCR->clsid = *pclsid;
g_psCR->mask |= CR_CLSID;
}
if (pidlRoot)
{
hmemcpy(&g_psCR->idlRoot, pidlRoot, uLen);
g_psCR->mask |= CR_IDLROOT;
}
}
v_hwndDesktop = CreateWindowEx(WS_EX_TOOLWINDOW,
c_szProxyDesktopClass, c_szProxyDesktopClass,
WS_POPUP, 0, 0, 0, 0, NULL, NULL, hInst, NULL);
if (!v_hwndDesktop)
{
Free(g_psCR);
return(FALSE);
}
FolderList_RegisterWindow(v_hwndDesktop,NULL);
return(TRUE);
}
typedef struct _FRDSTRUCT
{
HWND hwndDesktop;
HANDLE hCR;
DWORD dwProcId;
HWND hwndResult;
LPCOMPAREROOT lpcr;
} FRDSTRUCT;
BOOL CALLBACK FindRootEnumProc(HWND hwnd, LPARAM lParam)
{
FRDSTRUCT *pfrds;
TCHAR szClassName[40];
pfrds = (FRDSTRUCT *)lParam;
if (g_fRunSeparateDesktop)
{
if (hwnd == pfrds->hwndDesktop)
return TRUE; // Ignore the desktop always
}
GetClassName(hwnd, szClassName, ARRAYSIZE(szClassName));
if (lstrcmpi(szClassName, c_szProxyDesktopClass) != 0)
{
return(TRUE);
}
pfrds->lpcr->hwnd = hwnd;
if (SendMessage(pfrds->hwndDesktop, CWM_PERFORMCOMPARE,
(WPARAM)pfrds->dwProcId,
(LPARAM)pfrds->hCR ) == CR_SAME)
{
// Found it, so stop enumerating
pfrds->hwndResult = hwnd;
return(FALSE);
}
return(TRUE);
}
HWND FindRootedDesktop(const CLSID *pclsid, LPCITEMIDLIST pidlRoot)
{
HWND hwndDesktop;
TCHAR szClassName[40];
UINT uLen;
FRDSTRUCT frds;
// This is the "normal" desktop
hwndDesktop = GetShellWindow();
if (!g_fRunSeparateDesktop)
{
if (!pclsid && !pidlRoot)
{
if (hwndDesktop)
{
GetClassName(hwndDesktop, szClassName,
ARRAYSIZE(szClassName));
if (lstrcmpi(szClassName, c_szDesktopClass) == 0)
{
return(hwndDesktop);
}
}
}
}
frds.hwndDesktop = hwndDesktop;
frds.hwndResult = (HWND)0; // Initalize to no matching rooted expl
frds.dwProcId = GetCurrentProcessId();
frds.lpcr = FolderList_BuildCompare(NULL,pclsid,pidlRoot,NULL);
if (!frds.lpcr)
return (HWND)NULL; // Fail, not enough memory
frds.hCR = SHAllocShared(frds.lpcr,frds. lpcr->uSize, frds.dwProcId);
LocalFree(frds.lpcr); // We are done with this...
if (!frds.hCR)
{
// If this fails, something else will soon enough
return((HWND)0);
}
frds.lpcr = SHLockShared(frds.hCR, frds.dwProcId);
if (!frds.lpcr)
{
return((HWND)0);
}
EnumWindows(FindRootEnumProc, (LPARAM)&frds);
SHUnlockShared(frds.lpcr);
SHFreeShared(frds.hCR, frds.dwProcId);
return(frds.hwndResult);
}
BOOL Desktop_IsSameRoot(HWND hwnd, LPCITEMIDLIST pidlFolder)
{
LPCOMPAREROOT lpcr;
LPCLSID pclsid;
LPCITEMIDLIST pidlRoot;
HWND hwndDesktop = GetShellWindow();
DWORD dwProcId;
HANDLE hcr;
BOOL fResult;
if (g_psCR)
{
pclsid = (g_psCR->mask & CR_CLSID) ? &g_psCR->clsid : NULL;
pidlRoot = (g_psCR->mask & CR_IDLROOT) ? &g_psCR->idlRoot : NULL;
}
else
{
pclsid = NULL;
pidlRoot = NULL;
}
lpcr = FolderList_BuildCompare(hwnd,pclsid,pidlRoot,pidlFolder);
if (!lpcr)
return FALSE;
if (hwndDesktop)
{
dwProcId = GetCurrentProcessId();
hcr = SHAllocShared(lpcr,lpcr->uSize,dwProcId);
LocalFree(lpcr);
if (!hcr)
{
return FALSE; // If this fails, something else will soon enough
}
fResult = (SendMessage(hwndDesktop, CWM_PERFORMCOMPARE,
(WPARAM)dwProcId, (LPARAM)hcr) == CR_SAME);
SHFreeShared(hcr,dwProcId);
}
else
{
fResult = FolderList_PerformCompare(lpcr);
LocalFree(lpcr);
}
return fResult;
}
LPCITEMIDLIST Desktop_GetRootPidl(void)
{
if (!g_psCR || !(g_psCR->mask&CR_IDLROOT))
{
return(NULL);
}
return(&g_psCR->idlRoot);
}
HRESULT STDMETHODCALLTYPE CRef_QueryInterface(IUnknown *pu, REFIID riid, LPVOID *ppvObj)
{
if (IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = pu;
IUnknown_AddRef(pu);
return(NOERROR);
}
*ppvObj = NULL;
return(E_NOINTERFACE);
}
ULONG STDMETHODCALLTYPE CRef_AddRef(IUnknown *pu)
{
++g_cInstRef;
return(g_cInstRef);
}
ULONG STDMETHODCALLTYPE CRef_Release(IUnknown *pu)
{
--g_cInstRef; // Decrement the number of threads
// Let our desktop know that a thread is exiting...
PostMessage(v_hwndDesktop, DTM_THREADEXIT, 0, 0);
return(g_cInstRef);
}
#pragma data_seg(DATASEG_READONLY)
IUnknownVtbl s_RefUVtbl =
{
CRef_QueryInterface,
CRef_AddRef,
CRef_Release,
} ;
IUnknown g_unkRef = { &s_RefUVtbl } ;
#pragma data_seg()