MFC
壁紙の情報を取得する
※ 協力:ヴォーガ様

HomeProgramming TipsMFC Tips[MFC-014]

 ダウンロード : 壁紙表示サンプル(2005/08/14) 

壁紙の状態を取得するためには、まず、その環境がアクティブデスクトップか否かを
判定する必要があります。この判定に必要な関数ですが、IActiveDesktop を使うことになります。
IActiveDesktop は Microsoft が用意したアクティブデスクトップを扱うためのインターフェースです。
このインターフェースからオブジェクトを生成します。意味が分からない?気にしなくても使えます(笑)。

ここで MFC プログラムに組み込む際にハマったのがヘッダーの include です。
IActiveDesktop を使用するためには以下のヘッダを読み込む必要があります。


#include <wininet.h>
#include <shlobj.h>


ネットを検索するとかなり情報にヒットするのですが、ここにハマったという情報は見かけません。
よっぽど私はヘタレプログラマなのかと嘆いたものです (;´Д`)

ここでヴォーガ氏よりアドバイスを受けました。
MFC の仕様として include の順番には厳密さが要求される…と。
確かにネットにも include の順番が重要とは書いてあったけど、それだけではダメでした。

MFC では標準的に stdafx.h を作ります。ここには Windows のバージョン指定や
MFC で使用するために必要な各種ヘッダの読み込みが定義されています。
この中にある…


#include <afxdtctl.h>  // MFC の Internet Explorer 4 コモン コントロール サポート


という記述が曲者だったのです。
この afxdtctl.h の中には afxdisp.h が読み込まれており、
さらにその中には shlobj.h が読み込まれていたのです。つまり、
自分のプログラムの先頭にいくら順番通りヘッダ読み込みを指定しても
stdafx.h を読み込んだ時点で shlobj.h が読み込まれた後だった
ので
読み込み順序が違って IActiveDesktop が認識されなかったというわけです。

解決策としては、順番を守るために stdafx.h の afxdtctl.h の上に、wininet.h を記述すれば OK です。
stdafx.h の一部を抜粋します。


#include <afxwin.h>   // MFC のコアおよび標準コンポーネント
#include <afxext.h>   // MFC の拡張部分
#include <wininet.h>  // ← ここに追加しました。
#include <afxdtctl.h> // MFC の Internet Explorer 4 コモン コントロール サポート


長い前振りでしたが ^-^; これで IActiveDesktop が使えるようになりました。
ありがとうございます。 > ヴォーガ氏

さて、プログラミング手順です。
まず CoInitialize() で COM の初期化をします。
次に CoCreateInstance() で IID_IActiveDesktop のオブジェクトを生成します。
取得に成功すると IActiveDesktop のポインタが取得できます。
ここから GetDesktopItemOptions() で COMPONENTSOPT を取得して、fActiveDesktop を調べます。


// ---------------------------------------------------------------------
// アクティブデスクトップ環境か調べる
// ---------------------------------------------------------------------
BOOL CFooDlg::IsActiveDesktop()
{
    BOOL bActiveDesktop = FALSE;

    // COMPONENTSOPT 構造体のメンバをゼロで埋める
    COMPONENTSOPT  copt;
    ZeroMemory(&copt, sizeof(copt));
    copt.dwSize = sizeof(COMPONENTSOPT);

    // IActiveDesktop オブジェクトのインスタンスを作成
    CoInitialize (NULL);
    IActiveDesktop* pActiveDesktop;
    if SUCCEEDED(CoCreateInstance(
        CLSID_ActiveDesktop,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IActiveDesktop,
        (void**)&pActiveDesktop
    )) {

        // アクティブデスクトップが設定されているか
        if SUCCEEDED(pActiveDesktop->GetDesktopItemOptions(&copt, 0))
            bActiveDesktop = copt.fActiveDesktop ? TRUE : FALSE;

        // インスタンスを開放
        pActiveDesktop->Release();
    }
    CoUninitialize();
    return bActiveDesktop;
}


アクティブデスクトップだった場合は、同じく IActiveDesktop を使って各種情報を取得します。
取得されるパスは Unicode なので、必要に応じて MBCS に変換します。
サンプルとして以下のプログラムを提示します。
上記 IsActiveDesktop() は使っていませんのでご注意ください。


// ---------------------------------------------------------------------
// アクティブデスクトップ環境の壁紙情報を取得する
// ---------------------------------------------------------------------
BOOL CFooDlg::GetActiveDesktopWallpaper(
    CString& r_strPath,  // 壁紙のパス
    DWORD&   r_dwStyle   // 壁紙の設定状態
    // WPSTYLE_CENTER  : 中央に表示
    // WPSTYLE_TILE    : タイル表示
    // WPSTYLE_STRETCH : 拡大して表示
)
{
    BOOL bSuccess = FALSE;

    // COMPONENTSOPT 構造体のメンバを埋める
    COMPONENTSOPT  copt;
    ZeroMemory(&copt, sizeof(copt));
    copt.dwSize = sizeof(COMPONENTSOPT);

    // IActiveDesktop オブジェクトのインスタンスを作成
    CoInitialize (NULL);

    IActiveDesktop* pActiveDesktop;
    if SUCCEEDED(CoCreateInstance(
        CLSID_ActiveDesktop,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IActiveDesktop,
        (void**)&pActiveDesktop
    )) {

        // アクティブデスクトップが設定されているか
        if SUCCEEDED(pActiveDesktop->GetDesktopItemOptions(&copt, 0)) {

            // アクティブデスクトップなら
            if (copt.fActiveDesktop) {

                bSuccess = TRUE;

                // パスを取得する
                WCHAR wszPath[MAX_PATH + 1];
                if SUCCEEDED(pActiveDesktop->GetWallpaper(wszPath, MAX_PATH, 0)) {

                    // 文字コードを変換する
                    ::WideCharToMultiByte(
                        CP_ACP, 0,
                        (LPCWSTR)wszPath, int(wcslen(wszPath) + 1),
                        r_strPath.GetBuffer(MAX_PATH + 1), MAX_PATH,
                        NULL, NULL
                    );
                    r_strPath.ReleaseBuffer();
                }

                // タイル状態か確認する
                WALLPAPEROPT wpopt;
                wpopt.dwSize = sizeof(WALLPAPEROPT);
                if SUCCEEDED(pActiveDesktop->GetWallpaperOptions(&wpopt, 0))
                    r_dwStyle = wpopt.dwStyle;
            }
        }

        // 開放
        pActiveDesktop->Release();
    }
    CoUninitialize();

    return bSuccess;
}


これで壁紙のパスと設定状態が取得できます。
では、アクティブデスクトップではない場合はどうするのか。
当初、SPI_GETDESKWALLPAPER を使う方法を選択していましたが、この方法は Windows2000 以降専用です。
そのため、私としてはレジストリを直接参照する方法を推奨します。
MFC ならレジストリを扱うのは簡単ですからね。
必要な情報は HKEY_CURRENT_USER の Control Panel\\Desktop に記録されています。
以下にサンプルを提示します。CRegKey のおかげで意外なほど短いです。


// ---------------------------------------------------------------------
// 通常デスクトップ環境の壁紙を取得する
// ---------------------------------------------------------------------
BOOL CFooDlg::GetDesktopWallpaper(
    CString& r_strPath,  // 壁紙のパス
    DWORD&   r_dwStyle   // 壁紙の設定状態
    // WPSTYLE_CENTER  : 中央に表示
    // WPSTYLE_TILE    : タイル表示
    // WPSTYLE_STRETCH : 拡大して表示
)
{
    BOOL bSuccess = FALSE;

    // 壁紙の設定情報を直接レジストリから読み出す
    CRegKey reg;
    if (reg.Open(
        HKEY_CURRENT_USER,
        _T("Control Panel\\Desktop"),
        KEY_READ
    ) == ERROR_SUCCESS) {

        // 壁紙のパス
        ULONG ulLen = MAX_PATH;
        reg.QueryStringValue(
            _T("Wallpaper"),
            r_strPath.GetBuffer(MAX_PATH + 1),
            &ulLen
        );
        r_strPath.ReleaseBuffer();

        // タイル表示か?
        DWORD dwStyle;
        reg.QueryDWORDValue(_T("TileWallpaper"), dwStyle);
        if (dwStyle == _T('1')) {
            r_dwStyle = WPSTYLE_TILE;

        } else {

            // 拡大して表示か?
            reg.QueryDWORDValue(_T("WallpaperStyle"), dwStyle);
            r_dwStyle = (dwStyle == _T('2')) ? WPSTYLE_STRETCH : WPSTYLE_CENTER;
        }

        reg.Close();
    }
    return bSuccess;
}


※ 2005/08/14追記
 壁紙画像のパスが取得できても、そこに本当に壁紙ファイルがあるとは限りません。
 これにしばらく気がつかず、めちゃめちゃ苦労しました…。



 Copyright 2005 VALGUS. All Rights Reserved.