D言語 Advent Calendar 2025 9日目の記事です。
- D言語でMicrosoft Component Object Model (COM) を使う (その1) - ...ing logging 4.0
- D言語でMicrosoft Component Object Model (COM) を使う (その2) - ...ing logging 4.0
- D言語でMicrosoft Component Object Model (COM) を使う (その3) - ...ing logging 4.0
- D言語でMicrosoft Component Object Model (COM) を使う (その4) - ...ing logging 4.0
その1でCOMインタフェースを使うために必要な宣言や定義の準備ができて、 その2でCOMの初期化と解放ができるようになりました。 その3では実際にファイルを開くダイアログを表示することができ、その4では選択したファイルのパスを取得できました。
次に、IUnknownインタフェースのQueryInterface関数を使って、ひとつのCOMオブジェクトから別のCOMインタフェースを取得します。
まずは準備
IShellItem2 (shobjidl_core.h) - Win32 apps | Microsoft Learn
IShellItem2インタフェースには、GetFileTimeメソッドがあって、これを使うとファイルの日付と時刻を取得できるようです。
新しいCOMインタフェースが出てきたので、またShObjIdl_core.hを読んで自分で宣言します。
EXTERN_C const IID IID_IShellItem2; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("7e9fb0d3-919f-4307-ab2e-9b1860310c93") IShellItem2 : public IShellItem { public: virtual HRESULT STDMETHODCALLTYPE GetPropertyStore( /* [in] */ GETPROPERTYSTOREFLAGS flags, /* [in] */ __RPC__in REFIID riid, /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0; virtual HRESULT STDMETHODCALLTYPE GetPropertyStoreWithCreateObject( /* [in] */ GETPROPERTYSTOREFLAGS flags, /* [in] */ __RPC__in_opt IUnknown *punkCreateObject, /* [in] */ __RPC__in REFIID riid, /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0; virtual HRESULT STDMETHODCALLTYPE GetPropertyStoreForKeys( /* [size_is][in] */ __RPC__in_ecount_full(cKeys) const PROPERTYKEY *rgKeys, /* [in] */ UINT cKeys, /* [in] */ GETPROPERTYSTOREFLAGS flags, /* [in] */ __RPC__in REFIID riid, /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0; virtual HRESULT STDMETHODCALLTYPE GetPropertyDescriptionList( /* [in] */ __RPC__in REFPROPERTYKEY keyType, /* [in] */ __RPC__in REFIID riid, /* [iid_is][out] */ __RPC__deref_out_opt void **ppv) = 0; virtual HRESULT STDMETHODCALLTYPE Update( /* [unique][in] */ __RPC__in_opt IBindCtx *pbc) = 0; virtual HRESULT STDMETHODCALLTYPE GetProperty( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [out] */ __RPC__out PROPVARIANT *ppropvar) = 0; virtual HRESULT STDMETHODCALLTYPE GetCLSID( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [out] */ __RPC__out CLSID *pclsid) = 0; virtual HRESULT STDMETHODCALLTYPE GetFileTime( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [out] */ __RPC__out FILETIME *pft) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt32( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [out] */ __RPC__out int *pi) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [string][out] */ __RPC__deref_out_opt_string LPWSTR *ppsz) = 0; virtual HRESULT STDMETHODCALLTYPE GetUInt32( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [out] */ __RPC__out ULONG *pui) = 0; virtual HRESULT STDMETHODCALLTYPE GetUInt64( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [out] */ __RPC__out ULONGLONG *pull) = 0; virtual HRESULT STDMETHODCALLTYPE GetBool( /* [in] */ __RPC__in REFPROPERTYKEY key, /* [out] */ __RPC__out BOOL *pf) = 0; };
今使いたいのはGetFileTimeメソッドだけなので、これだけ完全な宣言にします。 使わないメソッドの定義は引数をなしにして宣言します。
使いたいメソッドの引数にある型は宣言する必要があります。
PROPERTYKEY (wtypes.h) - Win32 apps | Microsoft Learn
typedef struct _tagpropertykey { GUID fmtid; DWORD pid; } PROPERTYKEY;
REFPROPERTYKEYは、PROPERTYKEYのポインタ型なので、D言語では次のように宣言します。
struct PROPERTYKEY { GUID fmtid; DWORD pid; } alias REFPROPERTYKEY = PROPERTYKEY*;
FILETIME構造体は、core.sys.windows.winbaseにあるので、これをインポートするだけです。
ここまでで、D言語では次のようになります。
import core.sys.windows.winbase; struct PROPERTYKEY { GUID fmtid; DWORD pid; } alias REFPROPERTYKEY = PROPERTYKEY*; // uuid("7e9fb0d3-919f-4307-ab2e-9b1860310c93") interface IShellItem2 : IShellItem { extern(Windows): HRESULT GetPropertyStore();//(GETPROPERTYSTOREFLAGS flags, REFIID riid, void **ppv); HRESULT GetPropertyStoreWithCreateObject();//(GETPROPERTYSTOREFLAGS flags, IUnknown *punkCreateObject, REFIID riid, void **ppv); HRESULT GetPropertyStoreForKeys();//(const PROPERTYKEY *rgKeys, UINT cKeys, GETPROPERTYSTOREFLAGS flags, REFIID riid, void **ppv); HRESULT GetPropertyDescriptionList();//(REFPROPERTYKEY keyType, REFIID riid, void **ppv); HRESULT Update();//(IBindCtx *pbc); HRESULT GetProperty();//(REFPROPERTYKEY key, PROPVARIANT *ppropvar); HRESULT GetCLSID();//(REFPROPERTYKEY key, CLSID *pclsid); HRESULT GetFileTime(REFPROPERTYKEY key, FILETIME *pft); HRESULT GetInt32();//(REFPROPERTYKEY key, int *pi); HRESULT GetString();//(REFPROPERTYKEY key, LPWSTR *ppsz); HRESULT GetUInt32();//(REFPROPERTYKEY key, ULONG *pui); HRESULT GetUInt64();//(REFPROPERTYKEY key, ULONGLONG *pull); HRESULT GetBool();//(REFPROPERTYKEY key, BOOL *pf); }
GetFileTimeメソッドの第1引数にはPROPERYKEY変数に何らかの設定をしてから与えるようです。 今回はファイル作成日が欲しいので、System.DateCreatedを使えばいいようです。
System.DateCreated - Win32 apps | Microsoft Learn
propertyDescription
name = System.DateCreated
shellPKey = PKEY_DateCreated
formatID = B725F130-47EF-101A-A5F1-02608C9EEBAC
propID = 15
SearchInfo
IsColumn = true
typeInfo
type = DateTime
IsInnate = true
これを使ってもいいのですが、Windows SDKのpropkey.hで次のとおり定義されているので、こちらをベースにします。
// Name: System.DateCreated -- PKEY_DateCreated // Type: DateTime -- VT_FILETIME (For variants: VT_DATE) // FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 15 (PID_STG_CREATETIME) // // The date and time the item was created. The Indexing Service friendly name is 'create'. DEFINE_PROPERTYKEY(PKEY_DateCreated, 0xB725F130, 0x47EF, 0x101A, 0xA5, 0xF1, 0x02, 0x60, 0x8C, 0x9E, 0xEB, 0xAC, 15); #define INIT_PKEY_DateCreated { { 0xB725F130, 0x47EF, 0x101A, 0xA5, 0xF1, 0x02, 0x60, 0x8C, 0x9E, 0xEB, 0xAC }, 15 }
これをD言語で書くとこうなります。
const PROPERTYKEY PKEY_DateCreated = { { 0xB725F130, 0x47EF, 0x101A, [0xA5, 0xF1, 0x02, 0x60, 0x8C, 0x9E, 0xEB, 0xAC ]}, 15 };
ここまでで準備ができました。
QueryInterfaceを使って別のCOMインタフェースを取得
その4の記事のmain関数の後半を抜粋します。
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);
QueryInterfaceメソッドの第1引数でIID_IShellItem2を指定し、IShellItem2 COMインタフェースを要求します。 第2引数には、COMインタフェースを受け取るpItem2変数のアドレスを指定します。
// 上記の続きに書く IShellItem2 pItem2; hr = pItem.QueryInterface(&IID_IShellItem2, cast(void**)&pItem2); if (FAILED(hr)) return; scope(exit) pItem2.Release();
これで、IShellItem型のpItemから、IShellItem2型のpItem2を取得することができました。
使ってみる
PROPERTYKEY prop = PKEY_DateCreated; FILETIME filetime; SYSTEMTIME time; pItem2.GetFileTime(&prop, &filetime); FileTimeToSystemTime(&filetime, &time); writefln("%s-%s-%s", time.wYear, time.wMonth, time.wDay);
まずは、PROPERTYKEY型のprop変数に、PKEY_DateCreatedを代入して、ファイル作成日時を取得したいことを設定します。 QueryInterfaceメソッドで取得したIShellItem2 COMインタフェースのGetFileTimeを、pItem2を介して呼び出します。
IShellItem2::GetFileTime (shobjidl_core.h) - Win32 apps | Microsoft Learn
filetimeにはファイル時刻が格納されているので、FileTimeToSystemTime関数で我々が欲しいシステム時刻に変換します。
FILETIME (minwinbase.h) - Win32 apps | Microsoft Learn
実行してみる

ちゃんとファイル作成日時が取得できました。
以下、ソース全文です。
import std.stdio; import core.sys.windows.windef; import core.sys.windows.com; import core.sys.windows.objbase; import core.sys.windows.winbase; 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; extern(C) extern const IID IID_IShellItem2; struct PROPERTYKEY { GUID fmtid; DWORD pid; } alias REFPROPERTYKEY = PROPERTYKEY*; // uuid("7e9fb0d3-919f-4307-ab2e-9b1860310c93") interface IShellItem2 : IShellItem { extern(Windows): HRESULT GetPropertyStore();//(GETPROPERTYSTOREFLAGS flags, REFIID riid, void **ppv); HRESULT GetPropertyStoreWithCreateObject();//(GETPROPERTYSTOREFLAGS flags, IUnknown *punkCreateObject, REFIID riid, void **ppv); HRESULT GetPropertyStoreForKeys();//(const PROPERTYKEY *rgKeys, UINT cKeys, GETPROPERTYSTOREFLAGS flags, REFIID riid, void **ppv); HRESULT GetPropertyDescriptionList();//(REFPROPERTYKEY keyType, REFIID riid, void **ppv); HRESULT Update();//(IBindCtx *pbc); HRESULT GetProperty();//(REFPROPERTYKEY key, PROPVARIANT *ppropvar); HRESULT GetCLSID();//(REFPROPERTYKEY key, CLSID *pclsid); HRESULT GetFileTime(REFPROPERTYKEY key, FILETIME *pft); HRESULT GetInt32();//(REFPROPERTYKEY key, int *pi); HRESULT GetString();//(REFPROPERTYKEY key, LPWSTR *ppsz); HRESULT GetUInt32();//(REFPROPERTYKEY key, ULONG *pui); HRESULT GetUInt64();//(REFPROPERTYKEY key, ULONGLONG *pull); HRESULT GetBool();//(REFPROPERTYKEY key, BOOL *pf); } const PROPERTYKEY PKEY_DateCreated = { { 0xB725F130, 0x47EF, 0x101A, [0xA5, 0xF1, 0x02, 0x60, 0x8C, 0x9E, 0xEB, 0xAC ]}, 15 }; 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); IShellItem2 pItem2; hr = pItem.QueryInterface(&IID_IShellItem2, cast(void**)&pItem2); if (FAILED(hr)) return; scope(exit) pItem2.Release(); PROPERTYKEY prop = PKEY_DateCreated; FILETIME filetime; SYSTEMTIME time; pItem2.GetFileTime(&prop, &filetime); FileTimeToSystemTime(&filetime, &time); writefln("%s-%s-%s", time.wYear, time.wMonth, time.wDay); }