by July 3, 2002 0 comments



Ever wondered how when you create a folder or a file in an explorer window, does Windows get to know about it and quickly update its view? We’ll find this out by writing a program that will monitor Windows folders. Windows has two functions called SHChangeNotifyRegister and SHChangeNotifyDeregister that you can use in your application to know what is going on in your folder(s). These functions are undocumented, meaning that there is no information officially available to code them. We’ll develop a small application in VC++ that will inform you whenever a folder is created or renamed in your drives.

Internals
Whenever there is a change in the shell, Windows notifies itself by calling a SHChangeNotify (documented in MSDN). In our applications also, Windows expects us to call this function whenever we make some changes to the Shell. The key to trapping the folders is that our application is also notified when this function is called, and the answer to this is SHChangeNotifyRegister function, which looks something like this: 

HANDLE WINAPI SHChangeNotifyRegister( HWND hWnd, 
DWORD dwFlags, 
LONG wEventMask, 
UINT uMsg, 
DWORD cItems,
LPCNOTIFYREGISTER lpItems);

Here, the meaning of arguments is as follows: HWnd: Handle of the window to which the messages are sent.

DwFlags: Interrupt flag (should be Oring of 0x0001 and 0x0002)

WEventMask :  The events you want to catch, all the values are defined in the SHChangeNotify function in MSDN
uMsg : The message that would be sent to the application
cItems : Specifies the number of items you want to monitor
lpItems : the array of items you want to monitor, this would have a pidl and another BOOL flag which would tell that whether the monitor should be set on that directory only or on the whole subtree

Return value
If the function is successful, it would return the handle of the shell change notification. If it fails, it would return NULL.

SHChangeNotifyDeregister
SHChangeNotifyRegister function is exported from SHELL32.DLL with the ordinal value of 2. When we finish trapping the events, we call the function SHChangeNotifyDeregister, which has the ordinal value of 4. The hNotify is the Handle returned from the SHChangeNotifyRegister call, as follows:

BOOL WINAPI SHChangeNotifyDeregister(HANDLE hNotify);

Building an app
We will create an application that would reside in the System Tray and quietly monitor all changes. When our application is initiated, the dialog box would be hidden and a small icon in the system tray would appear, which would represent our application and the folder would be monitored till the application is closed. When you want to close the application, simply right-click the icon and select Exit from the menu (for creating the System Tray Icon, you can refer to the Shell_NotifyIcon function in MSDN).

First, create a basic MFC Application by selecting the MFC Appwizard (exe) from the New Projects window. To use the two functions from SHELL32.DLL you need to declare them in your application. At the top of your .CPP file, just declare the two functions and the structures needed as following:

#define SHCNF_ACCEPT_INTERRUPTS 0x0001
#define SHCNF_ACCEPT_NON_INTERRUPTS 0x0002
typedef struct {
LPCITEMIDLIST pidlPath;
BOOL bWatchSubtree;
} LPPIDLSTRUCT;
typedef struct {
DWORD dwItem1;
DWORD dwItem2;
} SHNOTIFYSTRUCT;
struct SHFILEINFOBYTE{
long hIcon;
long iIcon;
long dwAttributes;
byte szDisplayName[MAX_PATH];
byte szTypeName[80];
};
typedef WINSHELLAPI HANDLE (WINAPI *pfSHChangeNotifyRegister)(HWND hWnd,
DWORD dwFlags,
LONG wEventMask, 
UINT uMsg,
DWORD cItems,
LPPIDLSTRUCT *lpItems);
typedef WINSHELLAPI BOOL (WINAPI *pfSHChangeNotifyDeregister)(HANDLE hNotify);

Then in the OnInitDialog method, get the address of the SHChangeNotifyRegister function and call the specific function to registering your application with the shell. Here use one more function SHGetSpecialFolderLocation to get the PIDL of the drives. This function has predefined constants, which return the PIDL of some specific folders, like “My Documents”, “Desktop” etc.

C..Dlg::OnInitDialog() 
{


// TODO: Add extra validation here
/* Code for letting our application to register with the Windows and receive
* Shell Notification events 
* As the two functions cannot be accessed directly, we have to specifically load them
* from SHELL32.DLL
*/
m_hShell32 = LoadLibrary(“SHELL32.DLL”);
pfSHChangeNotifyRegister SHChangeNotifyRegister;
SHChangeNotifyRegister = (pfSHChangeNotifyRegister)GetProcAddress(m_hShell32,
MAKEINTRESOURCE(2));
LPPIDLSTRUCT stPIDL;
LPITEMIDLIST ppidl;
if(SHGetSpecialFolderLocation(GetSafeHwnd(),CSIDL_DRIVES, &ppidl) != NOERROR) {
AfxMessageBox(“GetSpecialFolder problem”);
}
stPIDL.pidlPath = ppidl;
stPIDL.bWatchSubtree = TRUE;
if( (m_hNotify = SHChangeNotifyRegister( GetSafeHwnd(), 
SHCNF_ACCEPT_INTERRUPTS | SHCNF_ACCEPT_NON_INTERRUPTS, 
/* We are only taking care of Directory Creation and Directory Rename */
SHCNE_MKDIR | SHCNE_RENAMEFOLDER, 
/* Message that would be sent by the Shell */
WM_SHELLNOTIFY, 
1,
&stPIDL)) == NULL) {
AfxMessageBox(“Change Register Failed”);
}
AfxMessageBox(“Change Register succeded”);

}

Declare the following function anywhere in the .CPP file. This function extracts the actual string path from the PIDLs sent by the windows through SHChangeNotify function.

CString GetPathFromPIDL(DWORD pidl) {
char sPath[MAX_PATH];
CString strTemp = “”;
if(SHGetPathFromIDList((struct _ITEMIDLIST *)pidl, sPath)) {
strTemp = sPath;
}
return strTemp;
}

Add this code to the message handler of the WM_SHELLNOTIFY message. The lParam of the message handler contains the event (SHCNE_.), and the wParam of the handler contains the SHNOTIFYSTRUCT. Here we are just opening a text file and writing the event notification along with the names. 

SHNOTIFYSTRUCT abc;
memcpy((void *)&abc,(void *)wParam,sizeof(SHNOTIFYSTRUCT));
if(lParam == SHCNE_RENAMEFOLDER) {
// Folder is being renamed
// The name of a folder has changed. 
// dwItem1 contains the previous PIDL or name of the folder. 
// dwItem2 contains the new PIDL or name of the folder. 
CString strBefore = GetPathFromPIDL(abc.dwItem1);
CString strAfter = GetPathFromPIDL(abc.dwItem2);
FILE *fp = fopen (“c:\\folderoperation.txt”, “a”);
char temp[(_MAX_PATH * 2) + 80];
if (fp) {
sprintf(temp,” Folder Rename – %s – %s\n”, strBefore, strAfter);
fwrite(temp, 1, strlen(temp), fp);
fclose(fp);
fp = NULL;
}
} else if(lParam == SHCNE_MKDIR) {
CString strFolderName = GetPathFromPIDL(abc.dwItem1);
FILE *fp = fopen (“c:\\folderoperation.txt”, “a”);
char temp[_MAX_PATH+80];
if (fp) {
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
sprintf(temp,”Folder Created – %s\n”, strFolderName);
fwrite(temp, 1, strlen(temp), fp);
fclose(fp);
fp = NULL;
}
}

In the Resource View, insert a new Menu and add a menu item as “Exit”. Your dialog’s exit button is in the handler. Next, just unregister your application to free the Shell32.dll. Now pass the handle returned by the SHChangeNotifyRegister to the SHChangeNotifyDeregister to unregister the application as follows:

pfSHChangeNotifyDeregister SHChangeNotifyDeregister;
SHChangeNotifyDeregister =
(pfSHChangeNotifyDeregister)GetProcAddress(m_hShell32,
MAKEINTRESOURCE(4));
SHChangeNotifyDeregister(m_hNotify);
FreeLibrary(m_hShell32);

As you can see, writing a Folder monitor is easy if you just know some Undocumented functions. This type of monitoring can also be done by creating a System Wide Hook and implementing a Virtual Device Driver. Next time you think of developing some security application that requires you to watch folder operations, you have the key with you.

Sameer Maggon, works with NIIT, at the Center for Research in
Cognitive Systems, IIT Delhi

No Comments so far

Jump into a conversation

No Comments Yet!

You can be the one to start a conversation.

Your data will be safe!Your e-mail address will not be published. Also other data will not be shared with third person.