672 lines
18 KiB
C++
672 lines
18 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
// This application demonstrates how to use the shell change notification system for
|
|
// watching a location in the shell namespace for changes.
|
|
// This can be used to keep track of the current state of a library since that
|
|
// is a shell container that participates in this system.
|
|
|
|
#define STRICT_TYPED_ITEMIDS // in case you do IDList stuff you want this on for better type saftey
|
|
#include <windows.h>
|
|
#include <strsafe.h>
|
|
#include "ShellHelpers.h"
|
|
#include "DragDropHelpers.h"
|
|
#include "ResizeableDialog.h"
|
|
#include "LogWindow.h"
|
|
#include "resource.h"
|
|
#include <new>
|
|
|
|
// This class encapsulates the registration and dispatching of shell change notification events
|
|
//
|
|
// To use this class:
|
|
// 1) Derive a class from this class. The derived class will implement the virtual
|
|
// method OnChangeNotify() that is called when the events occur.
|
|
//
|
|
// 2) Create an HWND and designate a message (in the WM_USER range) that will be used to
|
|
// dispatch the events. This HWND and MSG is passed to StartWatching() along with the
|
|
// item you want to watch.
|
|
//
|
|
// 3) In your window procedure, on receipt of the notification message, call OnChangeMessage().
|
|
//
|
|
// 4) Declare and implement OnChangeNotify() and write the code there that handles the events.
|
|
|
|
class CShellItemChangeWatcher
|
|
{
|
|
public:
|
|
CShellItemChangeWatcher() : _ulRegister(0)
|
|
{
|
|
}
|
|
|
|
~CShellItemChangeWatcher()
|
|
{
|
|
StopWatching();
|
|
}
|
|
|
|
// lEvents is SHCNE_XXX values like SHCNE_ALLEVENTS
|
|
// fRecursive means to listen for all events under this folder
|
|
HRESULT StartWatching(IShellItem *psi, HWND hwnd, UINT uMsg, long lEvents, BOOL fRecursive)
|
|
{
|
|
StopWatching();
|
|
|
|
PIDLIST_ABSOLUTE pidlWatch;
|
|
HRESULT hr = SHGetIDListFromObject(psi, &pidlWatch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SHChangeNotifyEntry const entries[] = { pidlWatch, fRecursive };
|
|
|
|
int const nSources = SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery;
|
|
_ulRegister = SHChangeNotifyRegister(hwnd, nSources, lEvents, uMsg, ARRAYSIZE(entries), entries);
|
|
hr = _ulRegister != 0 ? S_OK : E_FAIL;
|
|
|
|
CoTaskMemFree(pidlWatch);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
void StopWatching()
|
|
{
|
|
if (_ulRegister)
|
|
{
|
|
SHChangeNotifyDeregister(_ulRegister);
|
|
_ulRegister = 0;
|
|
}
|
|
}
|
|
|
|
// in your window procedure call this message to dispatch the events
|
|
void OnChangeMessage(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
long lEvent;
|
|
PIDLIST_ABSOLUTE *rgpidl;
|
|
HANDLE hNotifyLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &rgpidl, &lEvent);
|
|
if (hNotifyLock)
|
|
{
|
|
if (IsItemNotificationEvent(lEvent))
|
|
{
|
|
IShellItem2 *psi1 = NULL, *psi2 = NULL;
|
|
|
|
if (rgpidl[0])
|
|
{
|
|
SHCreateItemFromIDList(rgpidl[0], IID_PPV_ARGS(&psi1));
|
|
}
|
|
|
|
if (rgpidl[1])
|
|
{
|
|
SHCreateItemFromIDList(rgpidl[1], IID_PPV_ARGS(&psi2));
|
|
}
|
|
|
|
// derived class implements this method, that is where the events are delivered
|
|
OnChangeNotify(lEvent, psi1, psi2);
|
|
|
|
SafeRelease(&psi1);
|
|
SafeRelease(&psi2);
|
|
}
|
|
else
|
|
{
|
|
// dispatch non-item events here in the future
|
|
}
|
|
SHChangeNotification_Unlock(hNotifyLock);
|
|
}
|
|
}
|
|
|
|
// derived class implements this event
|
|
virtual void OnChangeNotify(long lEvent, IShellItem2 *psi1, IShellItem2 *psi2) = 0;
|
|
|
|
private:
|
|
|
|
bool IsItemNotificationEvent(long lEvent)
|
|
{
|
|
return !(lEvent & (SHCNE_UPDATEIMAGE | SHCNE_ASSOCCHANGED | SHCNE_EXTENDED_EVENT | SHCNE_FREESPACE | SHCNE_DRIVEADDGUI | SHCNE_SERVERDISCONNECT));
|
|
}
|
|
|
|
ULONG _ulRegister;
|
|
};
|
|
|
|
#define MAP_ENTRY(x) {L#x, x}
|
|
|
|
PCWSTR EventName(long lEvent)
|
|
{
|
|
PCWSTR psz = L"";
|
|
|
|
static const struct { PCWSTR pszName; long lEvent; } c_rgEventNames[] =
|
|
{
|
|
MAP_ENTRY(SHCNE_RENAMEITEM),
|
|
MAP_ENTRY(SHCNE_CREATE),
|
|
MAP_ENTRY(SHCNE_DELETE),
|
|
MAP_ENTRY(SHCNE_MKDIR),
|
|
MAP_ENTRY(SHCNE_RMDIR),
|
|
MAP_ENTRY(SHCNE_MEDIAINSERTED),
|
|
MAP_ENTRY(SHCNE_MEDIAREMOVED),
|
|
MAP_ENTRY(SHCNE_DRIVEREMOVED),
|
|
MAP_ENTRY(SHCNE_DRIVEADD),
|
|
MAP_ENTRY(SHCNE_NETSHARE),
|
|
MAP_ENTRY(SHCNE_NETUNSHARE),
|
|
MAP_ENTRY(SHCNE_ATTRIBUTES),
|
|
MAP_ENTRY(SHCNE_UPDATEDIR),
|
|
MAP_ENTRY(SHCNE_UPDATEITEM),
|
|
MAP_ENTRY(SHCNE_SERVERDISCONNECT),
|
|
MAP_ENTRY(SHCNE_DRIVEADDGUI),
|
|
MAP_ENTRY(SHCNE_RENAMEFOLDER),
|
|
MAP_ENTRY(SHCNE_FREESPACE),
|
|
MAP_ENTRY(SHCNE_UPDATEITEM),
|
|
};
|
|
for (int i = 0; i < ARRAYSIZE(c_rgEventNames); i++)
|
|
{
|
|
if (c_rgEventNames[i].lEvent == lEvent)
|
|
{
|
|
psz = c_rgEventNames[i].pszName;
|
|
break;
|
|
}
|
|
}
|
|
return psz;
|
|
}
|
|
|
|
PCWSTR StringToEmpty(PCWSTR psz)
|
|
{
|
|
return psz ? psz : L"";
|
|
}
|
|
|
|
HRESULT GetShellItemFromCommandLine(REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
int cArgs;
|
|
PWSTR *ppszCmd = CommandLineToArgvW(GetCommandLineW(), &cArgs);
|
|
if (ppszCmd && cArgs > 1)
|
|
{
|
|
WCHAR szSpec[MAX_PATH];
|
|
szSpec[0] = 0;
|
|
|
|
// skip all parameters that begin with "-" or "/" to try to find the
|
|
// file name. this enables parameters to be present on the cmd line
|
|
// and not get in the way of this function
|
|
for (int i = 1; (szSpec[0] == 0) && (i < cArgs); i++)
|
|
{
|
|
if ((*ppszCmd[i] != L'-') && (*ppszCmd[i] != L'/'))
|
|
{
|
|
StringCchCopyW(szSpec, ARRAYSIZE(szSpec), ppszCmd[i]);
|
|
PathUnquoteSpacesW(szSpec);
|
|
}
|
|
}
|
|
|
|
hr = szSpec[0] ? S_OK : E_FAIL; // protect against empty
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHCreateItemFromParsingName(szSpec, NULL, riid, ppv);
|
|
if (FAILED(hr))
|
|
{
|
|
WCHAR szFolder[MAX_PATH];
|
|
GetCurrentDirectoryW(ARRAYSIZE(szFolder), szFolder);
|
|
hr = PathAppendW(szFolder, szSpec) ? S_OK : E_FAIL;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHCreateItemFromParsingName(szFolder, NULL, riid, ppv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
usage:
|
|
|
|
CItemIterator itemIterator(psi);
|
|
|
|
while (itemIterator.MoveNext())
|
|
{
|
|
IShellItem2 *psi;
|
|
hr = itemIterator.GetCurrent(IID_PPV_ARGS(&psi));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
psi->Release();
|
|
}
|
|
}
|
|
*/
|
|
|
|
class CItemIterator
|
|
{
|
|
public:
|
|
CItemIterator(IShellItem *psi) : _hr(SHGetIDListFromObject(psi, &_pidlFull)), _psfCur(NULL)
|
|
{
|
|
_Init();
|
|
}
|
|
|
|
CItemIterator(PCIDLIST_ABSOLUTE pidl) : _hr(SHILCloneFull(pidl, &_pidlFull)), _psfCur(NULL)
|
|
{
|
|
_Init();
|
|
}
|
|
|
|
~CItemIterator()
|
|
{
|
|
CoTaskMemFree(_pidlFull);
|
|
SafeRelease(&_psfCur);
|
|
}
|
|
|
|
bool MoveNext()
|
|
{
|
|
bool fMoreItems = false;
|
|
|
|
if (SUCCEEDED(_hr))
|
|
{
|
|
if (NULL == _pidlRel)
|
|
{
|
|
fMoreItems = true;
|
|
_pidlRel = _pidlFull; // first item, might be empty if it is the desktop
|
|
}
|
|
else if (!ILIsEmpty(_pidlRel))
|
|
{
|
|
PCUITEMID_CHILD pidlChild = (PCUITEMID_CHILD)_pidlRel; // save the current segment for binding
|
|
_pidlRel = ILNext(_pidlRel);
|
|
|
|
// if we are not at the end setup for the next itteration
|
|
if (!ILIsEmpty(_pidlRel))
|
|
{
|
|
const WORD cbSave = _pidlRel->mkid.cb; // avoid cloning for the child by truncating temporarily
|
|
_pidlRel->mkid.cb = 0; // make this a child
|
|
|
|
IShellFolder *psfNew;
|
|
_hr = _psfCur->BindToObject(pidlChild, NULL, IID_PPV_ARGS(&psfNew));
|
|
if (SUCCEEDED(_hr))
|
|
{
|
|
_psfCur->Release();
|
|
_psfCur = psfNew; // transfer ownership
|
|
fMoreItems = true;
|
|
}
|
|
|
|
_pidlRel->mkid.cb = cbSave; // restore previous ID size
|
|
}
|
|
}
|
|
}
|
|
return fMoreItems;
|
|
}
|
|
|
|
HRESULT GetCurrent(REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
if (SUCCEEDED(_hr))
|
|
{
|
|
// create teh childID by truncating _pidlRel temporarily
|
|
PUIDLIST_RELATIVE pidlNext = ILNext(_pidlRel);
|
|
const WORD cbSave = pidlNext->mkid.cb; // save old cb
|
|
pidlNext->mkid.cb = 0; // make _pidlRel a child
|
|
|
|
_hr = SHCreateItemWithParent(NULL, _psfCur, (PCUITEMID_CHILD)_pidlRel, riid, ppv);
|
|
|
|
pidlNext->mkid.cb = cbSave; // restore old cb
|
|
}
|
|
return _hr;
|
|
}
|
|
|
|
HRESULT GetResult() const { return _hr; }
|
|
PCUIDLIST_RELATIVE GetRelativeIDList() const { return _pidlRel; }
|
|
|
|
private:
|
|
void _Init()
|
|
{
|
|
_pidlRel = NULL;
|
|
|
|
if (SUCCEEDED(_hr))
|
|
{
|
|
_hr = SHGetDesktopFolder(&_psfCur);
|
|
}
|
|
}
|
|
|
|
HRESULT _hr;
|
|
PIDLIST_ABSOLUTE _pidlFull;
|
|
PUIDLIST_RELATIVE _pidlRel;
|
|
IShellFolder *_psfCur;
|
|
};
|
|
|
|
// debugging helper that returns a string that represents the IDList in
|
|
// this form "[computer][C:][Foo][bar.txt]".
|
|
HRESULT GetIDListName(IShellItem *psi, PWSTR *ppsz)
|
|
{
|
|
*ppsz = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
WCHAR szFullName[2048];
|
|
szFullName[0] = 0;
|
|
PWSTR pszOutput = szFullName;
|
|
size_t cchOutput = ARRAYSIZE(szFullName);
|
|
|
|
CItemIterator itemIterator(psi);
|
|
while (itemIterator.MoveNext())
|
|
{
|
|
IShellItem2 *psi;
|
|
hr = itemIterator.GetCurrent(IID_PPV_ARGS(&psi));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
PWSTR pszName;
|
|
hr = psi->GetDisplayName(SIGDN_PARENTRELATIVE, &pszName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// ignore errors, this is for debugging only
|
|
StringCchCatEx(pszOutput, cchOutput, L"[", &pszOutput, &cchOutput, 0);
|
|
StringCchCatEx(pszOutput, cchOutput, pszName, &pszOutput, &cchOutput, 0);
|
|
StringCchCatEx(pszOutput, cchOutput, L"]", &pszOutput, &cchOutput, 0);
|
|
CoTaskMemFree(pszName);
|
|
}
|
|
|
|
psi->Release();
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHStrDup(szFullName, ppsz);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
enum GROUPID
|
|
{
|
|
GROUPID_DEFAULT,
|
|
GROUPID_NAMES,
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
GROUPID groupid;
|
|
PCWSTR pszGroupName;
|
|
} LOG_GROUP;
|
|
|
|
const LOG_GROUP c_rgGroupInfo[] =
|
|
{
|
|
{ GROUPID_NAMES, L"Event" },
|
|
};
|
|
|
|
const UINT c_notifyMessage = WM_USER + 200;
|
|
|
|
class CChangeNotifyApp : public CDragDropHelper, public CShellItemChangeWatcher
|
|
{
|
|
public:
|
|
CChangeNotifyApp() : _logWindow(c_rgGroupInfo, ARRAYSIZE(c_rgGroupInfo)),
|
|
_cRef(1), _hdlg(NULL), _psiDrop(NULL)
|
|
{
|
|
}
|
|
|
|
HRESULT DoModal(HWND hwnd)
|
|
{
|
|
DialogBoxParam(GetModuleHINSTANCE(), MAKEINTRESOURCE(IDD_DIALOG), hwnd, s_DlgProc, (LPARAM)this);
|
|
return S_OK;
|
|
}
|
|
|
|
// IUnknown
|
|
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CChangeNotifyApp, IDropTarget),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
IFACEMETHODIMP_(ULONG) AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
IFACEMETHODIMP_(ULONG) Release()
|
|
{
|
|
long cRef = InterlockedDecrement(&_cRef);
|
|
if (!cRef)
|
|
delete this;
|
|
return cRef;
|
|
}
|
|
|
|
void OnChangeNotify(long lEvent, IShellItem2 *psi1, IShellItem2 *psi2)
|
|
{
|
|
PWSTR pszLeft = NULL, pszRight = NULL;
|
|
|
|
if (psi1)
|
|
{
|
|
GetIDListName(psi1, &pszLeft);
|
|
}
|
|
|
|
if (psi2)
|
|
{
|
|
GetIDListName(psi2, &pszRight);
|
|
}
|
|
|
|
if (lEvent == SHCNE_RENAMEITEM || lEvent == SHCNE_RENAMEFOLDER)
|
|
{
|
|
_logWindow.LogMessagePrintf(GROUPID_NAMES, EventName(lEvent),
|
|
L"%s ==> %s", StringToEmpty(pszLeft), StringToEmpty(pszRight));
|
|
}
|
|
else
|
|
{
|
|
_logWindow.LogMessagePrintf(GROUPID_NAMES, EventName(lEvent),
|
|
L"%s , %s", StringToEmpty(pszLeft), StringToEmpty(pszRight));
|
|
}
|
|
|
|
CoTaskMemFree(pszLeft);
|
|
CoTaskMemFree(pszRight);
|
|
|
|
_logWindow.AutoAdjustListView();
|
|
}
|
|
|
|
private:
|
|
~CChangeNotifyApp()
|
|
{
|
|
_FreeItem();
|
|
}
|
|
|
|
static INT_PTR CALLBACK s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CChangeNotifyApp *pssa = reinterpret_cast<CChangeNotifyApp *>(GetWindowLongPtr(hdlg, DWLP_USER));
|
|
if (uMsg == WM_INITDIALOG)
|
|
{
|
|
pssa = reinterpret_cast<CChangeNotifyApp *>(lParam);
|
|
pssa->_hdlg = hdlg;
|
|
SetWindowLongPtr(hdlg, DWLP_USER, reinterpret_cast<LONG_PTR>(pssa));
|
|
}
|
|
return pssa ? pssa->_DlgProc(uMsg, wParam, lParam) : FALSE;
|
|
}
|
|
|
|
INT_PTR _DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
virtual HRESULT OnDrop(IShellItemArray *psia, DWORD grfKeyState);
|
|
|
|
void _OnInitDlg();
|
|
void _OnDestroyDlg();
|
|
|
|
void _PickItem()
|
|
{
|
|
IFileDialog *pfd;
|
|
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD dwOptions;
|
|
if (SUCCEEDED(pfd->GetOptions(&dwOptions)))
|
|
{
|
|
pfd->SetOptions(dwOptions | FOS_ALLNONSTORAGEITEMS | FOS_PICKFOLDERS);
|
|
}
|
|
|
|
pfd->SetTitle(L"Item Picker");
|
|
|
|
hr = pfd->Show(_hdlg);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IShellItem *psi;
|
|
hr = pfd->GetResult(&psi);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_FreeItem();
|
|
|
|
if (SUCCEEDED(psi->QueryInterface(&_psiDrop)))
|
|
{
|
|
_StartWatching();
|
|
}
|
|
psi->Release();
|
|
}
|
|
}
|
|
|
|
pfd->Release();
|
|
}
|
|
}
|
|
|
|
void _StartWatching()
|
|
{
|
|
_logWindow.ResetContents();
|
|
|
|
BOOL const fRecursive = IsDlgButtonChecked(_hdlg, IDC_RECURSIVE);
|
|
StartWatching(_psiDrop, _hdlg, c_notifyMessage, SHCNE_ALLEVENTS, fRecursive);
|
|
|
|
PWSTR pszName;
|
|
if (SUCCEEDED(_psiDrop->GetDisplayName(SIGDN_NORMALDISPLAY, &pszName)))
|
|
{
|
|
WCHAR szTitle[128];
|
|
StringCchPrintf(szTitle, ARRAYSIZE(szTitle), L"Watching - %s", pszName);
|
|
|
|
_logWindow.LogMessagePrintf(GROUPID_NAMES, L"Watching", L"%s %s", pszName, fRecursive ? L"(Recursive)" : L"");
|
|
_logWindow.AutoAdjustListView();
|
|
|
|
SetWindowText(_hdlg, szTitle);
|
|
CoTaskMemFree(pszName);
|
|
}
|
|
}
|
|
|
|
void _FreeItem()
|
|
{
|
|
SafeRelease(&_psiDrop);
|
|
}
|
|
|
|
long _cRef;
|
|
HWND _hdlg;
|
|
IShellItem2 *_psiDrop;
|
|
CLogWindow<LOG_GROUP, GROUPID> _logWindow;
|
|
|
|
static const ANCHOR c_rgAnchors[5];
|
|
RECT _rgAnchorOffsets[ARRAYSIZE(c_rgAnchors)];
|
|
};
|
|
|
|
const ANCHOR CChangeNotifyApp::c_rgAnchors[] =
|
|
{
|
|
{ IDC_PICK, AF_LEFT | AF_BOTTOM },
|
|
{ IDC_LISTVIEW, AF_LEFT | AF_RIGHT | AF_TOP | AF_BOTTOM },
|
|
{ IDC_RECURSIVE, AF_RIGHT | AF_BOTTOM },
|
|
{ IDC_GRIPPER, AF_RIGHT | AF_BOTTOM },
|
|
};
|
|
|
|
HRESULT CChangeNotifyApp::OnDrop(IShellItemArray *psia, DWORD)
|
|
{
|
|
_FreeItem();
|
|
|
|
// hold the dropped item for later in _psiDrop
|
|
HRESULT hr = GetItemAt(psia, 0, IID_PPV_ARGS(&_psiDrop));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_StartWatching();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
void CChangeNotifyApp::_OnInitDlg()
|
|
{
|
|
InitResizeData(_hdlg, c_rgAnchors, ARRAYSIZE(c_rgAnchors), _rgAnchorOffsets);
|
|
SetDialogIcon(_hdlg, SIID_APPLICATION);
|
|
|
|
InitializeDragDropHelper(_hdlg, DROPIMAGE_LABEL, L"Listen for Changes on %1");
|
|
|
|
_logWindow.InitListView(GetDlgItem(_hdlg, IDC_LISTVIEW));
|
|
|
|
// optional cmd line param
|
|
if (SUCCEEDED(GetShellItemFromCommandLine(IID_PPV_ARGS(&_psiDrop))))
|
|
{
|
|
_StartWatching();
|
|
}
|
|
}
|
|
|
|
void CChangeNotifyApp::_OnDestroyDlg()
|
|
{
|
|
ClearDialogIcon(_hdlg);
|
|
TerminateDragDropHelper();
|
|
|
|
StopWatching();
|
|
}
|
|
|
|
INT_PTR CChangeNotifyApp::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL bRet = TRUE; // default for all handled cases in switch below
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
_OnInitDlg();
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
const int idCmd = GET_WM_COMMAND_ID(wParam, lParam);
|
|
switch (idCmd)
|
|
{
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
return EndDialog(_hdlg, idCmd);
|
|
|
|
case IDC_RECURSIVE:
|
|
if (_psiDrop)
|
|
{
|
|
_StartWatching();
|
|
}
|
|
break;
|
|
|
|
case IDC_PICK:
|
|
_PickItem();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR *pnm = (NMHDR*)lParam;
|
|
if (pnm->idFrom == IDC_LISTVIEW)
|
|
{
|
|
_logWindow.OnNotify(pnm);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
OnSize(_hdlg, c_rgAnchors, ARRAYSIZE(c_rgAnchors), _rgAnchorOffsets);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
_OnDestroyDlg();
|
|
break;
|
|
|
|
case c_notifyMessage:
|
|
// This is the message that will be sent when changes are detected in the shell namespace
|
|
OnChangeMessage(wParam, lParam);
|
|
break;
|
|
|
|
default:
|
|
bRet = FALSE;
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
|
{
|
|
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CChangeNotifyApp *pdlg = new (std::nothrow) CChangeNotifyApp();
|
|
if (pdlg)
|
|
{
|
|
pdlg->DoModal(NULL);
|
|
pdlg->Release();
|
|
}
|
|
CoUninitialize();
|
|
}
|
|
return 0;
|
|
}
|