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.     
 

解决方案 »

  1.   

    NT and Memory Maps 
     
    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).