I want to show the Windows file properties dialog for a file from my C++ code (on Windows 7, using VS 2012). I found the following code in this answer (which also contains a full MCVE). I also tried calling CoInitializeEx()
first, as mentioned in the documentation of ShellExecuteEx()
:
// Whether I initialize COM or not doesn't seem to make a difference.
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO info = {0};
info.cbSize = sizeof info;
info.lpFile = L"D:\\Test.txt";
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
info.lpVerb = L"properties";
ShellExecuteEx(&info);
This code works, i.e. the properties dialog is shown and ShellExecuteEx()
returns TRUE
. However, in the Details tab, the size property is wrong and the date properties are missing:
The rest of the properties in the Details tab (e.g. the file attributes) are correct. Strangely, the size and date properties are shown correctly in the General tab (left-most tab).
If I open the properties window via the Windows Explorer (file → right-click → Properties), then all properties in the Details tab are shown correctly:
I tried it with several files and file types (e.g. txt, rtf, pdf) on different drives and on three different PCs (1x German 64-bit Windows 7, 1x English 64-bit Windows 7, 1x English 32-bit Windows 7). I always get the same result, even if I run my program as administrator. On (64-bit) Windows 8.1 the code is working for me, though.
My original program in which I discovered the problem is an MFC application, but I see the same problem if I put the above code into a console application.
What do I have to do to show the correct values in the Details tab on Windows 7? Is it even possible?
As Raymond Chen suggested, replacing the path with a PIDL (
SHELLEXECUTEINFO::lpIDList
) makes the properties dialog correctly show the size and date fields under Windows 7 when invoked throughShellExecuteEx()
.It seems that the Windows 7 implementation of
ShellExecuteEx()
is buggy since newer versions of the OS do not have an issue withSHELLEXCUTEINFO::lpFile
.There is another solution possible that involves creating an instance of
IContextMenu
and calling theIContextMenu::InvokeCommand()
method. I guess this is whatShellExecuteEx()
does under the hood. Scroll down to Solution 2 for example code.Solution 1 - using a PIDL with ShellExecuteEx
This code works for me under both Win 7 and Win 10 (other versions not tested) when called from a button click handler of a simple dialog-based MFC application.
It also works for console applications if you set
info.hwnd
toNULL
(simply remove the lineinfo.hwnd = GetSafeHwnd();
from the example code as it is already initialized with 0). In the SHELLEXECUTEINFO reference it is stated that thehwnd
member is optional.Don't forget the mandatory call to
CoInitialize()
orCoInitializeEx()
at the startup of your application andCoUninitialize()
at shutdown to properly initialize and deinitialize COM.Notes:
CComHeapPtr
is a smart pointer included in ATL that automatically callsCoTaskMemFree()
when the scope ends. It's an ownership-transferring pointer with semantics similar to the deprecatedstd::auto_ptr
. That is, when you assign aCComHeapPtr
object to another one, or use the constructor that has aCComHeapPtr
parameter, the original object will become a NULL pointer.I'm still using it because it is ready to use out-of-the-box and plays well together with the COM APIs.
Solution 2 - using IContextMenu
The following code requires Windows Vista or newer as I'm using the "modern"
IShellItem
API.I wrapped the code into a function
ShowPropertiesDialog()
that takes a window handle and a filesystem path. If any error occurs, the function throws astd::system_error
exception.In the following I show an example of how to use
ShowPropertiesDialog()
from a button handler of a CDialog-derived class. ActuallyShowPropertiesDialog()
is independent from MFC, as it just needs a window handle, but OP mentioned that he wants to use the code in an MFC app.