A t some time or another you may have stumbled across
the function SHChangeNotify. According to the docs, xxxxxxxxxxxxxxx
it notifies the system of any events that may affect the
shell. Well that's all very nice for the shell, but what about
the rest of us? Wouldn't it be nice if we could get to see
those notifications too?
The answer is the SHChangeNotifyRegister function.
It can be used to register a window with the shell, which
will then be notified of all subsequent SHChangeNotify
events. It's exported from SHELL32.DLL with an ordinal
value of 2. The function declaration looks like this:
HANDLE WINAPI SHChangeNotifyRegister(
HWND hWnd,
DWORD dwFlags,
LONG wEventMask,
UINT uMsg,
DWORD cItems,
LPCNOTIFYREGISTER lpItems);
wEventMask is a mask of all the events you are in-
terested in. You can use any combination of the SHCNE_
constants from the SHChangeNotify function.
dwFlags allows you to filter out interrupt and non-
interrupt events (see Figure 1). I'm not exactly sure what
an interrupt event is (other than
that it uses the SHCNE_INTER-
RUPT flag), but typically both
SHCNF_ACCEPT_ flags should be
set (i.e. accept any type of
event). The SHCNF_NO_PROXY
flag allows you to handle the no-
SHCNF_ACCEPT_INTERRUPTS 0x0001
SHCNF_ACCEPT_NON_INTERRUPTS 0x0002
SHCNF_NO_PROXY 0x8000
Figure 1 SHChangeNotifyRegister Flags
tification more efficiently on Windows NT, but it compli-
cates the callback and requires a few more undocumented
functions. I'll explain the whole process later, but for the
moment it's best left alone.
lpItems is an array of items that you want to monitor
and cItems specifies the number
of items in the array (see Figure 2
for the structure definition). If the
pidlPath specifies a folder, you
will receive events for the folder
itself as well as any items in the
folder. If you set bWatchSubtree
typedef struct {
LPCITEMIDLIST pidlPath;
BOOL bWatchSubtree;
} NOTIFYREGISTER;
Figure 2 Item Structure
to TRUE, you will receive events for the entire substree
rooted at that folder (i.e. all folders and items below the
specified folder). There must be at least one item in the
lpItems array, but you can set the pidlPath to NULL, in
which case you will receive events for all items on the
system.
hWnd specifies the handle of the window that should
receive any notifications, and uMsg is the message that will
be sent to that window. It is recommended that you use a
WM_USER message for uMsg. The details of the message
handling is explained later.
If the function succeeds, the return value is a handle of
a shell change notification object. If the function fails, the
return value is NULL. When you're finished monitoring the
notification events you should pass the handle to
SHChangeNotifyDeregister. The function declaration looks
like this:
BOOL WINAPI SHChangeNotifyDeregister(
HANDLE hNotify);
Obviously enough, if the function succeeds, the return
value is TRUE. If the function fails, the return value is
FALSE. The function's ordinal value is 4.
The Notification Message
When your window receives the notification message, the
lParam will be set to the id of the event that occured (one
of the SHCNE_ values), and the wParam will point to an
array containing the two PIDLs associated with the event.
You may be wondering why you only receive PIDLs, even
though the SHChangeNotify function permits any number
of data types. The reason is that all the data types are
automatically converted into PIDLs before being sent any-
where.
For SHCNF_PATH and SHCNF_PRINTER types this
seems obvious enough. For the SHCNF_DWORD type, a 10
byte pidl is created with the two DWORD items immediately
following the size (see Figure 3). The SHCNE_
FREESPACE event is handled as a special case.
When the drive is passed in as a PATH or an
IDLIST it is converted into a DWORD, with
drives A to Z mapping onto bits 0 to 25. For
example drive D would map to bit 3, so the
DWORD would have a value of 8. If there are
two drives specified for the event, then two bits
struct {
USHORT cb;
DWORD dwItem1;
DWORD dwItem2;
} DWORDITEMID;
Figure 3 Format of DWORD Types
will be set in the DWORD. The value of the second item in
the SHCNE_FREESPACE event appears to be meaningless.
the function SHChangeNotify. According to the docs, xxxxxxxxxxxxxxx
it notifies the system of any events that may affect the
shell. Well that's all very nice for the shell, but what about
the rest of us? Wouldn't it be nice if we could get to see
those notifications too?
The answer is the SHChangeNotifyRegister function.
It can be used to register a window with the shell, which
will then be notified of all subsequent SHChangeNotify
events. It's exported from SHELL32.DLL with an ordinal
value of 2. The function declaration looks like this:
HANDLE WINAPI SHChangeNotifyRegister(
HWND hWnd,
DWORD dwFlags,
LONG wEventMask,
UINT uMsg,
DWORD cItems,
LPCNOTIFYREGISTER lpItems);
wEventMask is a mask of all the events you are in-
terested in. You can use any combination of the SHCNE_
constants from the SHChangeNotify function.
dwFlags allows you to filter out interrupt and non-
interrupt events (see Figure 1). I'm not exactly sure what
an interrupt event is (other than
that it uses the SHCNE_INTER-
RUPT flag), but typically both
SHCNF_ACCEPT_ flags should be
set (i.e. accept any type of
event). The SHCNF_NO_PROXY
flag allows you to handle the no-
SHCNF_ACCEPT_INTERRUPTS 0x0001
SHCNF_ACCEPT_NON_INTERRUPTS 0x0002
SHCNF_NO_PROXY 0x8000
Figure 1 SHChangeNotifyRegister Flags
tification more efficiently on Windows NT, but it compli-
cates the callback and requires a few more undocumented
functions. I'll explain the whole process later, but for the
moment it's best left alone.
lpItems is an array of items that you want to monitor
and cItems specifies the number
of items in the array (see Figure 2
for the structure definition). If the
pidlPath specifies a folder, you
will receive events for the folder
itself as well as any items in the
folder. If you set bWatchSubtree
typedef struct {
LPCITEMIDLIST pidlPath;
BOOL bWatchSubtree;
} NOTIFYREGISTER;
Figure 2 Item Structure
to TRUE, you will receive events for the entire substree
rooted at that folder (i.e. all folders and items below the
specified folder). There must be at least one item in the
lpItems array, but you can set the pidlPath to NULL, in
which case you will receive events for all items on the
system.
hWnd specifies the handle of the window that should
receive any notifications, and uMsg is the message that will
be sent to that window. It is recommended that you use a
WM_USER message for uMsg. The details of the message
handling is explained later.
If the function succeeds, the return value is a handle of
a shell change notification object. If the function fails, the
return value is NULL. When you're finished monitoring the
notification events you should pass the handle to
SHChangeNotifyDeregister. The function declaration looks
like this:
BOOL WINAPI SHChangeNotifyDeregister(
HANDLE hNotify);
Obviously enough, if the function succeeds, the return
value is TRUE. If the function fails, the return value is
FALSE. The function's ordinal value is 4.
The Notification Message
When your window receives the notification message, the
lParam will be set to the id of the event that occured (one
of the SHCNE_ values), and the wParam will point to an
array containing the two PIDLs associated with the event.
You may be wondering why you only receive PIDLs, even
though the SHChangeNotify function permits any number
of data types. The reason is that all the data types are
automatically converted into PIDLs before being sent any-
where.
For SHCNF_PATH and SHCNF_PRINTER types this
seems obvious enough. For the SHCNF_DWORD type, a 10
byte pidl is created with the two DWORD items immediately
following the size (see Figure 3). The SHCNE_
FREESPACE event is handled as a special case.
When the drive is passed in as a PATH or an
IDLIST it is converted into a DWORD, with
drives A to Z mapping onto bits 0 to 25. For
example drive D would map to bit 3, so the
DWORD would have a value of 8. If there are
two drives specified for the event, then two bits
struct {
USHORT cb;
DWORD dwItem1;
DWORD dwItem2;
} DWORDITEMID;
Figure 3 Format of DWORD Types
will be set in the DWORD. The value of the second item in
the SHCNE_FREESPACE event appears to be meaningless.
解决方案 »
- 关于GetPixel()的问题请教。
- vbscrip 调用 ocx
- 简单问题 ! 关于ACTIVE注册
- _variant_t类型怎么转化为LPCTSTR啊? 各位帮忙
- richeditctrl控件中移动光标
- 大连HP话务员5000??(散分)
- 求教:AddNew以后Update的问题
- CList 添加节点的问题!在线等,问题解决,就给分!高手帮忙啊!急急急急
- 谁知道如何在atl写的dll中实现重载啊?!!?!详细内容入内!
- Ring0驱动层如何获取模块Version
- wndclass.hbrBackground=GetStockObject(WHITE_BRUSH); 这句为什么老出错?????
- 为何注册为服务程序时编译出错?
This all seems simple enough, but on NT there is a bit of a
problem. You can't just send a message to a window in a
different process, and still expect the structure pointer
contained in that message to be accessible. To get around
this, NT actually dumps all the data into a memory-mapped
file, and then sends the memory-map handle and a process
id as the parameters to the message.
In order to remain compatible with Windows 95, some-
body obviously has to extract the information from that
memory-map on the other side. The way this works is that
NT creates a hidden 'proxy' window whenever you call
SHChangeNotifyRegister. It is the proxy window that
receives the notification message containing the memory-
map. Its message handler then extracts all the information,
before passing on the expected data structure to your win-
dow.
Of course, this is not exactly efficient, which is where
the SHCNF_NO_PROXY flag comes in. By specifying that
flag, you are telling NT not to create the proxy window, so
the memory-map handle gets passed directly to your win-
dow. It's then up to you to extract the relevant information
from the memory-map. Fortunately there are two functions
that do all the work for you:
HANDLE WINAPI SHChangeNotification_Lock(
HANDLE hMemoryMap,
DWORD dwProcessId,
LPCITEMIDLIST **lppidls,
LPLONG lpwEventId);
BOOL WINAPI SHChangeNotification_Unlock(
HANDLE hLock);
You call SHChangeNotification_Lock, passing it the
memory map handle and the process id that you received in
the notification message's wParam and lParam respec-
tively. lppidls points to a variable that receives the LPCITE-
MIDLIST pointer that is normally passed in the notification's
wParam. lpdwEventId points to a variable that receives the
event id that is normally passed in the lParam. If you aren't
interested in the PIDLs or the event id you can set either of
those parameters to NULL.
The return value is a handle that you pass to
SHChangeNotification_Unlock when you're finished
handling the notifica-
tion. If you don't quite
follow the explanation,
have a look at the
example code fragment
in Figure 4.
The ordinal values
for the two functions
are 644 and 645.
However, they do not
exist under Windows
95, so if you want to
use them (and you still
want to support Win-
dows 95), you'll need to
link to them at run-time
with GetProcAddress.
LPCITEMIDLIST *pidls;
LONG wEventId;
if (bRunningWindowsNT) {
HANDLE hLock;
hLock = SHChangeNotification_Lock(
(HANDLE)wParam, (DWORD)lParam,
&pidls, &wEventId);
if (hLock) {
ProcessEvent(wEventId,
pidls[0], pidls[1]);
SHChangeNotification_Unlock(hLock);
}
}
else {
pidls = (LPCITEMIDLIST*)wParam;
wEventId = (LONG)lParam;
ProcessEvent(wEventId,
pidls[0], pidls[1]);
}
Figure 4 Example Notification Handler
The Origin of
Events
So now, you know how to receive any of these shell notifi-
cations that are floating around, but who is actually genera-
ting them? According to the documentation, 'An application
should use this function (SHChangeNotify) if it performs an
action that may affect the shell'. But that seems to be a bit
of wishful thinking. I can't imagine there are many applica-
tion developers that really give a damn whether the shell is
kept informed of their actions.
Fortunately the shell seems to generate most of the
notifications itself. Sometimes it may be directly responsible
for an event, in which case it is easy enough for it to make
a call to SHChangeNotify. However, for things like file
creation, that are quite likely to originate in another applica-
tion, it would assumedly have to be monitoring the system
with FindFirstChangeNotification in order to generate the
event.
The result is that these notifications tend to be a little bit
unreliable. The likelyhood of you getting an event for some-
thing, may depend on what explorer windows happen to be
open at the time. The shell also only has a 10 item event
buffer, and may replace some events with a generic
SHCNE_UPDATEDIR in case of an overflow. In short: don't
depend on these notifications for any mission-critical appli-
cations.
Don't Believe Everything You Read
Another problem with the SHChangeNotify function is that
the documentation is not always completely accurate in its
descriptions of the various events. For example, SHCNE_
ATTRIBUTES is supposed to happen when 'the attributes of
an item or folder have changed'. However, I have only ever
witnessed an SHCNE_ATTRIBUTES event when my printer
status changed. Changing file and folder attributes just
produces an SHCNE_UPDATEITEM event.
SHCNE_NETSHARE and SHCNE_NETUNSHARE are
supposed to occur when you share or unshare a folder.
However, on Windows NT the SHCNE_NETUNSHARE
never happens. You get a SHCNE_NETSHARE event on both
occasions. On Windows 95 they appear to work as adver-
tised though.
An SHCNE_UPDATEIMAGE event apparently signifies
that an image in the system image list has changed. Item1
of the function is supposed to contain the index in the sys-
tem image list that has changed. However, images in the
system image list shouldn't ever change. What the event
really means is that an application, that was using that parti-
cular icon index in the system image list, is now using
something else.
Typical uses of SHCNE_UPDATEIMAGE include the
recycle bin (changing between empty and full), and the icon
associated with a CD drive. Document icons that change as
a result of a file type association do not generate an
SHCNE_UPDATEIMAGE. They will produce an SHCNE_
ASSOCCHANGED event instead.
I don't really know that much about how the shell uses
notifications. I'm just mentioning some of the descrepencies
I've encountered while experimenting with SHChange-
NotifyRegister. If you want to know more, it's probably
best that you test them yourself (and test on as many plat-
forms as possible).