Home > Software engineering >  How to safely get the new filename from SHNotify event (SHCNE_RENAMEITEM)
How to safely get the new filename from SHNotify event (SHCNE_RENAMEITEM)

Time:04-26

I've hooked into the Windows shell, and am receiving notification events nicely to my hidden CWnd.

When I receive the SHCNE_RENAMEITEM event, the SHGetPathFromIDList() function returns me the old file name, but not the new file name of the rename.

I've successfully hacked a pointer and discovered the new name, but this does not feel very safe to me. I tried using ILNext/ILGetNext/ILIsEmpty to iterate the list, but these do not give me the new name. Is there a safer way?

afx_msg LRESULT OnChange(WPARAM wParam, LPARAM lParam)
{
   long lEvent = 0L;
   PIDLIST_ABSOLUTE* rgpidl=nullptr;
   TCHAR szFileOld[MAX_PATH] = L"\0";
   TCHAR szFileNew[MAX_PATH] = L"\0";
        
   HANDLE hNotifyLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &rgpidl, &lEvent);

   if (!SHGetPathFromIDListW((struct _ITEMIDLIST*)*rgpidl, szFileOld))
      return TRUE;

   if (lEvent & SHCNE_RENAMEITEM)
   {    
      struct _ITEMIDLIST* pNext = (struct _ITEMIDLIST*)*(&rgpidl[1]); // yes, I got lucky guessing the synatx.

      if (ILIsEmpty(pNext)) // probably not much safety on this, but trying to be kind.
         return TRUE;

      if (!SHGetPathFromIDListW(pNext, szFileNew)) 
        return TRUE;
   }
// other code.
   return FALSE;
}

I should mention my registration code is using the New Delivery method.

    BOOL fRecursive = FALSE;
    UINT uMsg = WM_FILE_CHANGED;
    long lEvents = SHCNE_UPDATEITEM | SHCNE_DELETE | SHCNE_CREATE | SHCNE_RENAMEITEM | SHCNE_UPDATEDIR;
    int const nSources = SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery;
    SHChangeNotifyEntry const entries[] = { pidlWatch, fRecursive };

    m_lNotificationRegistry = SHChangeNotifyRegister(m_pWnd->m_hWnd, nSources, lEvents, uMsg, ARRAYSIZE(entries), entries);

CodePudding user response:

You are on the right track, but your syntax is just over-complicated. SHChangeNotification_Lock() will give you a pointer to an array of PIDLs, you don't need fancy type-casts to access the elements of that array.

Also, you need to call SHChangeNotification_Unlock() before exiting your callback function.

Try something more like this instead:

afx_msg LRESULT OnChange(WPARAM wParam, LPARAM lParam)
{
    long lEvent = 0L;
    PIDLIST_ABSOLUTE* rgpidl = nullptr;

    HANDLE hNotifyLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &rgpidl, &lEvent);

    if (lEvent & SHCNE_RENAMEITEM)
    {
        WCHAR szFileOld[MAX_PATH] = L"\0";
        WCHAR szFileNew[MAX_PATH] = L"\0"; 
        SHGetPathFromIDListW(rgpidl[0], szFileOld); 
        SHGetPathFromIDListW(rgpidl[1], szFileNew);

    }
    // other code.

    SHChangeNotification_Unlock(hNotifyLock);

    return FALSE;
} 
  • Related