Windowsのごみ箱アイコンの変化を検知する

# ごみ箱の ITEMIDLIST を取得
```c++
#include <ShlObj.h>

LPITEMIDLIST pidlBitBucket;
SHGetSpecialFolderLocation(nullptr, CSIDL_BITBUCKET, &pidlBitBucket);
```
[SHGetSpecialFolderLocation function (shlobj_core.h) - Win32 apps | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows/win32/api/shlobj_core/nf-shlobj_core-shgetspecialfolderlocation)
# シェルの変更通知に登録
```c++
#define WM_SHCHANGENOTIFY (WM_APP + 1)

SHChangeNotifyEntry changeNotifyEntry = { 0 };
changeNotifyEntry.fRecursive = FALSE;
changeNotifyEntry.pidl = pidlBitBucket;

ULONG ulID = SHChangeNotifyRegister(
    hWnd,
    (SHCNRF_ShellLevel | SHCNRF_NewDelivery),
    SHCNE_UPDATEIMAGE,
    WM_SHCHANGENOTIFY,
    1,
    &changeNotifyEntry);
```
[SHChangeNotifyRegister function (shlobj_core.h) - Win32 apps | Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shchangenotifyregister)
`SHChangeNotifyRegister`の第3引数(`fEvents`)に`SHCNE_UPDATEIMAGE`を渡すと、アイコンの変更が通知されます。
# Window メッセージ を処理
```c++
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_SHCHANGENOTIFY:
            LPITEMIDLIST* ppidl;
            LONG lEvent;
            HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
        
            if (!hLock) {
                break;
            }

            if (lEvent == SHCNE_UPDATEIMAGE) {
                AnalyzeIcon();
            }

            SHChangeNotification_Unlock(hLock);
            break;

        ...
    }

    return 0;
}
```
[SHChangeNotification_Lock function (shlobj_core.h) - Win32 apps | Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shchangenotification_lock)
[SHChangeNotification_Unlock function (shlobj_core.h) - Win32 apps | Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shchangenotification_unlock)
# ごみ箱のアイコンを取得
```c++
#include <shellapi.h>

void AnalyzeIcon()
{
    SHFILEINFO shFileInfo = { 0 };

    if (SUCCEEDED(SHGetFileInfo(
        (LPCTSTR)pidlBitBucket,
        -1,
        &shFileInfo,
        sizeof(shFileInfo),
        (SHGFI_PIDL | SHGFI_ICON)))) {
        HICON hIcon = shFileInfo.hIcon;
        // アイコンを使う処理
        ...

        if (hIcon) {
            DestroyIcon(hIcon);
        }
    }
}
```
[SHGetFileInfoA function (shellapi.h) - Win32 apps | Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfoa)
`SHGetFileInfo`の第1引数(`pszPath`)に`ITEMIDLIST`のポインタ、第5引数に`SHGFI_PIDL`を渡すのがポイント。
取得したアイコンのハンドルは破棄が必要。
# サンプル
```c++
#include <ShlObj.h>
#include <shellapi.h>
#define WM_SHCHANGENOTIFY (WM_APP + 1)

LPITEMIDLIST g_pidlBitBucket = nullptr;
ULONG g_ulID = 0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_CREATE:
            CoInitialize(nullptr);

            {
                SHChangeNotifyEntry changeNotifyEntry = { 0 };
                changeNotifyEntry.fRecursive = FALSE;
                changeNotifyEntry.pidl = g_pidlBitBucket;

                g_ulID = SHChangeNotifyRegister(
                    hWnd,
                    (SHCNRF_ShellLevel | SHCNRF_NewDelivery),
                    SHCNE_UPDATEIMAGE,
                    WM_SHCHANGENOTIFY,
                    1,
                    &changeNotifyEntry);

                if (!g_ulID) {
                    break;
                }
            }

            break;

        case WM_DESTROY:
            SHChangeNotifyDeregister(g_ulID);

            if (g_pidlBitBucket) {
                CoTaskMemFree(g_pidlBitBucket);
            }

            CoUninitialize();
            PostQuitMessage(0);
            break;

        case WM_SHCHANGENOTIFY:
        {
            LPITEMIDLIST* ppidl;
            LONG lEvent;
            HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);

            if (!hLock) {
                break;
            }

            if (lEvent == SHCNE_UPDATEIMAGE) {
                AnalyzeIcon();
            }

            SHChangeNotification_Unlock(hLock);
        }

        break;

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}
```
# 余談
.NETではshell32.dllを参照に追加するだけで、こんなコードが書けます。

```csharp
using Shell32;
using System;

static class Program
{
    static void Main()
    {
        Shell shell = new Shell();
        // ShellSpecialFolderConstants::ssfBITBUCKET: 10
        Folder bitBucketFolder = shell.NameSpace(10);

        foreach (FolderItem2 item in bitBucketFolder.Items())
        {
            Console.WriteLine(item.Name);
        }
    }
}
```
[Windows Shell - Win32 apps | Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/shell/shell-entry)

## 追記
`IShellFolder`は使用しないので、説明を削除しました。
(ごみ箱の中身を検証する時に使用します)