...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

D言語でMicrosoft Component Object Model (COM) を使う (その4)

D言語 Advent Calendar 2025 8日目の記事です。

その1でCOMインタフェースを使うために必要な宣言や定義の準備ができて、 その2でCOMの初期化と解放ができるようになりました。 その3では実際にファイルを開くダイアログを表示することができました。

COMインタフェースの使用

選択されたファイルパスを取得するためには、 IFileOpenDialogのGetResultメソッドを呼び出す必要があります。

IFileDialog::GetResult (shobjidl_core.h) - Win32 apps | Microsoft Learn

HRESULT GetResult(IShellItem* ppsi);

結果がIShellItemインタフェースで返されるので、まずはこれを受け取ります。

IShellItem pItem;
hr = pDialog.GetResult(&pItem);
if (FAILED(hr)) return;
scope(exit) pItem.Release();

その3の記事で述べたように、COMオブジェクトの解放にはscope(exit)を使っておきます。

続いて、返されたIShlellItemインタフェースのGetDisplayNameメソッドを呼び出して、 ファイルパスを文字列で取得します。

IShellItem::GetDisplayName (shobjidl_core.h) - Win32 apps | Microsoft Learn

ppszName が指す文字列が不要になったときに解放するのは、呼び出し元の責任です。 *ppszName で CoTaskMemFree を呼び出して、メモリを解放します。

最後にCoTaskMemFree関数を呼び出して文字列のメモリを解放することを忘れないようにします。

LPWSTR outPath;
hr = pItem.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &outPath);
if (FAILED(hr)) return;
// ここでoutPath使う。
CoTaskMemFree(outPath);

これでoutPathには文字列が入ってきますが、 writeln関数で簡単に表示させるためにstd.conv.toでstring型に変換してしまいます。

import std.conv;
writeln(outPath.to!string);

実行してみる

C:\d\gitproj\dfl\examples\toggleswitch\shell.bat

のとおり、選択したファイルのパスが表示できました。

以下、ソース全文です。

import std.stdio;
import core.sys.windows.windef;
import core.sys.windows.com;
import core.sys.windows.objbase;

extern(C) extern const IID IID_IModalWindow;

// uuid("b4db1657-70d7-485e-8e3e-6fcb5a5c1802")
interface IModalWindow : IUnknown
{
extern (Windows):
    HRESULT Show(HWND hwndOwner);
}

extern(C) extern const IID IID_IFileDialog;

// uuid("42f85136-db7e-439c-85f1-e4075d135fc8")
interface IFileDialog : IModalWindow
{
extern (Windows):
    HRESULT SetFileTypes();//(UINT cFileTypes, const COMDLG_FILTERSPEC* rgFilterSpec);
    HRESULT SetFileTypeIndex();//(UINT iFileType);
    HRESULT GetFileTypeIndex();//(UINT* piFileType);
    HRESULT Advise();//(IFileDialogEvents pfde, DWORD* pdwCookie);
    HRESULT Unadvise();//(DWORD dwCookie);
    HRESULT SetOptions();//(FILEOPENDIALOGOPTIONS fos);
    HRESULT GetOptions();//(FILEOPENDIALOGOPTIONS * pfos);
    HRESULT SetDefaultFolder();//(IShellItem psi);
    HRESULT SetFolder();//(IShellItem psi);
    HRESULT GetFolder();//(IShellItem* ppsi);
    HRESULT GetCurrentSelection();//(IShellItem* ppsi);
    HRESULT SetFileName();//(LPCWSTR pszName);
    HRESULT GetFileName();//(LPWSTR pszName);
    HRESULT SetTitle();//(LPCWSTR pszTitle);
    HRESULT SetOkButtonLabel();//(LPCWSTR pszText);
    HRESULT SetFileNameLabel();//(LPCWSTR pszLabel);
    HRESULT GetResult(IShellItem* ppsi);
    HRESULT AddPlace();//(IShellItem psi, FDAP fdap);
    HRESULT SetDefaultExtension();//(LPCWSTR pszDefaultExtension);
    HRESULT Close();//(HRESULT hr);
    HRESULT SetClientGuid();//(REFGUID guid);
    HRESULT ClearClientData();
    HRESULT SetFilter();//(IShellItemFilter pFilter);
}

extern(C) extern const IID IID_IFileOpenDialog;

// uuid("d57c7288-d4ad-4768-be02-9d969532d960")
interface IFileOpenDialog : IFileDialog
{
extern(Windows):
    HRESULT GetResults();//(IShellItemArray* ppenum);
    HRESULT GetSelectedItems();//(IShellItemArray* ppsai);
}

extern(C) extern const IID IID_IShellItem;

// uuid("43826d1e-e718-42ee-bc55-a1e261c37bfe")
interface IShellItem : IUnknown
{
extern(Windows):
    HRESULT BindToHandler();//(IBindCtx pbc, REFGUID bhid, REFIID riid, void** ppv);
    HRESULT GetParent();//(IShellItem* ppsi);
    HRESULT GetDisplayName(SIGDN sigdnName, LPWSTR* ppszName);
    HRESULT GetAttributes();//(SFGAOF sfgaoMask, SFGAOF* psfgaoAttribs);
    HRESULT Compare();//(IShellItem psi, SICHINTF hint, int* piOrder);
}

enum SIGDN : int
{
    SIGDN_NORMALDISPLAY = 0,
    SIGDN_PARENTRELATIVEPARSING = 0x80018001,
    SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
    SIGDN_PARENTRELATIVEEDITING = 0x80031001,
    SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
    SIGDN_FILESYSPATH = 0x80058000,
    SIGDN_URL = 0x80068000,
    SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
    SIGDN_PARENTRELATIVE = 0x80080001,
    SIGDN_PARENTRELATIVEFORUI = 0x80094001
}

extern(C) extern const CLSID CLSID_FileOpenDialog;

void main()
{
    HRESULT hr;

    hr = CoInitializeEx(null, COINIT.COINIT_MULTITHREADED);
    if (FAILED(hr)) return;
    scope(exit) CoUninitialize();

    IFileOpenDialog pDialog;
    hr = CoCreateInstance(&CLSID_FileOpenDialog, null, CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, cast(void**)&pDialog);
    if (FAILED(hr)) return;
    scope(exit) pDialog.Release();

    hr = pDialog.Show(null);
    if (FAILED(hr)) return;

    IShellItem pItem;
    hr = pDialog.GetResult(&pItem);
    if (FAILED(hr)) return;
    scope(exit) pItem.Release();

    LPWSTR outPath;
    hr = pItem.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &outPath);
    if (FAILED(hr)) return;
    scope(exit) CoTaskMemFree(outPath);

    import std.conv;
    writeln(outPath.to!string);
}