Purpose :
New folder creation function in Windows Explorer (Desktop)
(Icon coordinate setting + label correction)
I am wondering how to create a new folder like Windows Explorer (Desktop).
I want to know the same function as "Right click on desktop → New → Folder".
What I want is to make it to specific coordinates.
++ It would be better if you could also modify the label.
This is how I did it.
CreateDirectory → FindWindow (Desktop Listview) → SendMessage (LVM_SETITEMPOSITION)
But when I do CreateDirectory it appears too slow on the desktop.
(Default 1000ms or more)
So I tried SHChangeNotify but it appears slow.
That source code functions to move the mouse to a specific location on the desktop and press a shortcut to create a folder with that coordinate.
However, I can see the folder appearing slow and moving (SendMessage (LVM_SETITEMPOSITION)).
// this is Console Application
HWND hDesk = FindWindow(NULL, L"Program Manager");
HWND hDesk2 = FindWindowEx(hDesk, NULL, L"SHELLDLL_DefView", NULL);
HWND hListView = FindWindowEx(hDesk2, NULL, L"SysListView32", NULL);
int iconCount = (int)SendMessage(hListView, LVM_GETITEMCOUNT, NULL, NULL);
printf("컨트롤(Control) + F2\n");
RegisterHotKey(NULL, 1, MOD_CONTROL, VK_F2);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
PeekMessage(&msg, 0, 0, 0, 0x0001);
switch (msg.message)
{
case WM_HOTKEY:
if (msg.wParam == 1)
{
printf("ㅇㅋ 누름 (Hotkey Event)\n");
int a = SendMessage(hListView, LVM_GETITEMCOUNT, 0, 0);
int b;
int ms = GetTickCount();
CreateDirectory(L"C:\\Users\\root\\Desktop\\new Folder", NULL);
//SendMessage(hListView, LVM_REDRAWITEMS, 0, a+5);
//SendMessage(hListView, LVM_UPDATE, a, 0);
//SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST| SHCNF_PATHW, L"C:\\Users\\root\\Desktop", NULL);
while (true)
{
b = SendMessage(hListView, LVM_GETITEMCOUNT, 0, 0);
if (a != b) {
ms = GetTickCount() - ms;
break;
}
}
Sleep(1);
POINT mouse;
GetCursorPos(&mouse);
SendMessage(hListView, LVM_SETITEMPOSITION, b - 1, MAKEWPARAM(mouse.x, mouse.y));
SendMessage(hListView, LVM_EDITLABEL, b - 1, 0);
}
}
}
I thought CreateDirectory was the problem and tried to create a directory using NtCreateFile (User Space / Ring3), but it is still slow.
The icon still appears slow on the desktop.
Is there WinAPI / DesktopAPI / COM / ATL for this?
Here is a code that simulates what's the New... Folder menu does from a Console Application.
It opens a view on some folder and create a child folder in it, and enter name edit mode. It's using the Shell's IFileOperation interface and also ATL's smart pointers only for simplicity.
int main()
{
CoInitialize(NULL);
{
CComHeapPtr<ITEMIDLIST> pidl;
CComHeapPtr<ITEMIDLIST> newFolderPidl;
CComPtr<IFileOperationProgressSink> sink;
CComPtr<IFileOperation> fo;
CComPtr<IShellItem> folder;
LPCITEMIDLIST pidls = { nullptr };
CFileOperationProgressSink csink;
// get some folder's PIDL
HRESULT hr = SHParseDisplayName(L"c:\\temp", NULL, &pidl, 0, NULL);
if (SUCCEEDED(hr))
{
// open it and, for demo purposes here, selects nothing (pass a terminator PIDL)
ITEMIDLIST idl = { 0 };
pidls = { &idl };
hr = SHOpenFolderAndSelectItems(pidl, 1, &pidls, 0);
if (SUCCEEDED(hr))
{
// get a Shell Item from this PIDL
hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&folder));
}
}
if (SUCCEEDED(hr))
{
// we want to operate on files and directory using Shell's API
hr = fo.CoCreateInstance(CLSID_FileOperation);
if (SUCCEEDED(hr))
{
csink.QueryInterface(IID_PPV_ARGS(&sink));
// create the new folder "New Folder", using the sink
// we need a sink to be advised of what really is the new folder
// we can't use the name we've passed
// because it could have been changed like 'New Folder (2)' if there's already a 'New Folder', etc.
hr = fo->NewItem(folder, FILE_ATTRIBUTE_DIRECTORY, L"New Folder", NULL, sink);
if (SUCCEEDED(hr))
{
// commit
hr = fo->PerformOperations();
}
}
}
if (SUCCEEDED(hr))
{
// we want the new child's relative-to-the-parent PIDL
CComPtr<IParentAndItem> pai;
hr = csink.m_newItem->QueryInterface(&pai);
if (SUCCEEDED(hr))
{
hr = pai->GetParentAndItem(NULL, NULL, &newFolderPidl);
}
}
if (SUCCEEDED(hr))
{
// now, select the new child and get into name-edit mode (from the parent again)
pidls = { newFolderPidl };
hr = SHOpenFolderAndSelectItems(pidl, 1, &pidls, OFASI_EDIT);
}
}
CoUninitialize();
return 0;
}
// the sink. this implementation's only interested by PostNewItem
class CFileOperationProgressSink : public IFileOperationProgressSink
{
LONG m_cRef;
public:
CComPtr<IShellItem> m_newItem;
CFileOperationProgressSink() : m_cRef(1)
{
}
STDMETHOD(QueryInterface)(REFIID riid, void** ppv)
{
static const QITAB qit[] =
{
QITABENT(CFileOperationProgressSink, IFileOperationProgressSink),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHOD_(ULONG, AddRef)() { return InterlockedIncrement(&m_cRef); }
STDMETHOD_(ULONG, Release)()
{
if (InterlockedDecrement(&m_cRef))
return m_cRef;
delete this;
return 0;
}
STDMETHOD(StartOperations)() { return S_OK; };
STDMETHOD(FinishOperations)(HRESULT hrResult) { return S_OK; };
STDMETHOD(PreRenameItem)(DWORD dwFlags, IShellItem* psiItem, LPCWSTR pszNewName) { return S_OK; };
STDMETHOD(PostRenameItem)(DWORD dwFlags, IShellItem* psiItem, LPCWSTR pszNewName, HRESULT hrRename, IShellItem* psiNewlyCreated) { return S_OK; };
STDMETHOD(PreMoveItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName) { return S_OK; };
STDMETHOD(PostMoveItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName, HRESULT hrMove, IShellItem* psiNewlyCreated) { return S_OK; };
STDMETHOD(PreCopyItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName) { return S_OK; };
STDMETHOD(PostCopyItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName, HRESULT hrCopy, IShellItem* psiNewlyCreated) { return S_OK; };
STDMETHOD(PreDeleteItem)(DWORD dwFlags, IShellItem* psiItem) { return S_OK; };
STDMETHOD(PostDeleteItem)(DWORD dwFlags, IShellItem* psiItem, HRESULT hrDelete, IShellItem* psiNewlyCreated) { return S_OK; };
STDMETHOD(PreNewItem)(DWORD dwFlags, IShellItem* psiDestinationFolder, LPCWSTR pszNewName) { return S_OK; };
STDMETHOD(PostNewItem)(DWORD dwFlags, IShellItem* psiDestinationFolder, LPCWSTR pszNewName, LPCWSTR pszTemplateName, DWORD dwFileAttributes, HRESULT hrNew, IShellItem* psiNewItem)
{
if (SUCCEEDED(hrNew))
{
psiNewItem->QueryInterface(&m_newItem);
}
return S_OK;
}
STDMETHOD(UpdateProgress)(UINT iWorkTotal, UINT iWorkSoFar) { return S_OK; };
STDMETHOD(ResetTimer)() { return S_OK; };
STDMETHOD(PauseTimer)() { return S_OK; };
STDMETHOD(ResumeTimer)() { return S_OK; };
};
Related
Consider the following code:
bool ListFolderContent(const std::wstring& fileName)
{
PIDLIST_ABSOLUTE pidl = nullptr;
if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr)))
return false;
IShellFolder* pShellfolder = nullptr;
LPCITEMIDLIST pidlRelative = nullptr;
HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative);
if (FAILED(hr))
{
::CoTaskMemFree(pidl);
return false;
}
IEnumIDList* pEnumIDList = nullptr;
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnumIDList);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
while (1)
{
LPITEMIDLIST pChild = nullptr;
hr = pEnumIDList->Next(1, &pChild, nullptr);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
if (hr == S_FALSE)
break;
wchar_t buffer[MAX_PATH + 1];
if (::SHGetPathFromIDListW(pChild, buffer))
{
::OutputDebugString(buffer);
::OutputDebugString(L"\r\n");
}
}
pShellfolder->Release();
::CoTaskMemFree(pidl);
return true;
}
This code works well and logs the content of the folder owning the given file I pass through the fileName parameter.
However I have an issues with this code: Whatever I pass as file name, the logged path is always C:\Users\Admin\Desktop, and NOT the path to the parent folder I'm iterating, as expected. On the other hand the file names are correct.
Can someone explain me what I'm doing wrong?
You are passing just the last component ("filename") here: SHGetPathFromIDListW(pChild, buffer). Since the desktop is the root you are basically asking to get the filesystem path of [Desktop] [Filename] from the namespace.
There are two solutions:
pShellfolder->GetDisplayNameOf(pChild, SHGDN_FORPARSING, ...)+StrRetToBuf. This is fast since you already have an instance of the folder interface.
Call SHGetPathFromIDList with an absolute (full) pidl. On the pidl from SHILCreateFromPath, call ILCloneFull, ILRemoveLastID and ILCombine(clone, pChild).
HRESULT Example()
{
WCHAR buf[MAX_PATH];
GetWindowsDirectory(buf, MAX_PATH);
PathAppend(buf, L"Explorer.exe");
PIDLIST_ABSOLUTE pidl, pidlFullItem;
HRESULT hr = SHILCreateFromPath(buf, &pidl, nullptr); // %windir%\Explorer.exe
if (FAILED(hr)) return hr;
LPITEMIDLIST pLeaf;
IShellFolder*pShellfolder; // %windir%
hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, nullptr);
if (SUCCEEDED(hr))
{
IEnumIDList*pEnum;
// Method 1:
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
STRRET sr;
hr = pShellfolder->GetDisplayNameOf(pLeaf, SHGDN_FORPARSING, &sr);
if (SUCCEEDED(hr))
{
hr = StrRetToBuf(&sr, pLeaf, buf, MAX_PATH);
if (SUCCEEDED(hr)) wprintf(L"M1: Item: %s\n", buf);
}
}
pEnum->Release();
}
// Method 2:
ILRemoveLastID(pidl); // %windir%\Explorer.exe => %windir%
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
pidlFullItem = ILCombine(pidl, pLeaf); // %windir% + Filename
if (pidlFullItem)
{
hr = SHGetPathFromIDListW(pidlFullItem, buf);
if (SUCCEEDED(hr)) wprintf(L"M2: Item: %s\n", buf);
ILFree(pidlFullItem);
}
}
pEnum->Release();
}
pShellfolder->Release();
}
ILFree(pidl);
return hr;
}
I have my custom shell namespace extension. Just want to have a virtual folder mapped to some folder on disk C:/ with the same functionality.
using namespace ATL;
class ATL_NO_VTABLE CMyShellFolder :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CMyShellFolder, &CLSID_CMyShellFolder>,
public IPersistFolder2,
public IShellFolder2,
public IExplorerPaneVisibility
{
CComHeapPtr<ITEMIDLIST_ABSOLUTE> m_pidl;
CComPtr<IShellFolder2> m_folder;
CComPtr<IThumbnailHandlerFactory> m_thFactory;
CComPtr<IUnknown> m_site;
public:
CMyShellFolder()
{
}
static HRESULT WINAPI UpdateRegistry(BOOL reg) throw();
BEGIN_COM_MAP(CMyShellFolder)
COM_INTERFACE_ENTRY(IShellFolder)
COM_INTERFACE_ENTRY(IShellFolder2)
COM_INTERFACE_ENTRY2(IPersist, IPersistFolder)
COM_INTERFACE_ENTRY(IPersistFolder)
COM_INTERFACE_ENTRY(IPersistFolder2)
COM_INTERFACE_ENTRY(IExplorerPaneVisibility)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
---------------------------------
HRESULT CMyShellFolder::BindToObject(PCUIDLIST_RELATIVE pidl, IBindCtx* pbc, REFIID riid, LPVOID* ppv)
{
if (riid == __uuidof(IShellFolder3))
return E_NOINTERFACE;
HR;
CComObject<CMyShellFolder>* folder = nullptr;
CHECKARG(pidl);
hr = E_NOINTERFACE;
if (riid == IID_IShellFolder ||
riid == IID_IShellFolder2)
{
// check it's a folder
SFGAOF atts = SFGAO_FOLDER;
auto hr2 = GetAttributesOf(1, (PCUITEMID_CHILD_ARRAY)&pidl, &atts);
if (FAILED(hr2) || !(atts & SFGAO_FOLDER))
goto cleanup; // nope, get out
CHECKHR(CreateInstanceAddRef(&folder));
CHECKHR(folder->_Initialize(this, pidl));
CHECKHR(folder->QueryInterface(riid, ppv));
}
RELEASE(folder);
HRONFAIL(L"CMyShellFolder::BindToObject"); }
HRESULT CMyShellFolder::CreateViewObject(HWND hwnd, REFIID riid, LPVOID* ppv)
{
HR;
SFV_CREATE sfvc = { 0 };
DEFCONTEXTMENU dcm = { 0 };
CHECKITEM;
CHECKARG(ppv);
hr = E_NOINTERFACE;
if (riid == IID_IShellView)
{
sfvc.cbSize = sizeof(SFV_CREATE);
CHECKHR(QueryInterface(IID_PPV_ARGS(&sfvc.pshf)));
QueryInterface(IID_PPV_ARGS(&sfvc.psfvcb));
CHECKHR(SHCreateShellFolderView(&sfvc, (IShellView**)ppv));
goto cleanup;
}
if (riid == IID_IContextMenu)
{
dcm.hwnd = hwnd;
//dcm.pidlFolder = (PCIDLIST_ABSOLUTE)m_pidl.m_pData;
QueryInterface(IID_PPV_ARGS(&dcm.pcmcb));
CHECKHR(QueryInterface(IID_PPV_ARGS(&dcm.psf)));
CHECKHR(SHCreateDefaultContextMenu(&dcm, riid, ppv));
goto cleanup;
}
if (riid == IID_ITransferSource || // for delete & file operations
riid == IID_IDropTarget) // for copy paste & dd
{
CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));
goto cleanup;
}
CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));
cleanup:
RELEASE(dcm.pcmcb);
RELEASE(dcm.psf);
RELEASE(sfvc.psfvcb);
RELEASE(sfvc.pshf);
HRONFAIL(L"CMyShellFolder::CreateViewObject"); }
But opening images from my custom folders don't work.
image image2
Actually it displays an image for about a second and then error message appears.
Nevertheless it could be opened normally via Paint
Your nse need handle IID_Istream to open file with UWP program
// Pseudo code
STDMETHODIMP NSEShellFolder::BindToObject (LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, void** ppvOut)
{
HRESULT hr = E NOTIMPL;
//.....//
if (riid == IID IStream) {
DWORD grfMode = 0;
if (pbcReserved) { //IBindCtx
BIND OPTS opts;
ZeroMemory (&opts, sizeof (BIND OPTS));
opts.cbStruct = sizeof (opts);
pbc->GetBindOptions (&opts);
grfMode = opts.grfMode;
}
//If your nse Has FileSystemPath, Win32 API ¹SHCreateStreamOnFile Ex' is useful.
//or you can implement own IStream interface.
LPWSTR pPath = CNSEData::GetPath (m_pidl);
BOOL bCreate = grfMode & STGM CREATE? TRUE : FALSE;
hr = SHCreateStreamOnFileEx (pPath,grfMode, FILE_ATTRIBUTE_NORMAL,bCreate,NULL, (IStream**)ppvOut);
}
//.....//
return hr;
}
Before
After
I am working on something that pulls up a context menu. I want to be able to close the app while leaving open things like "properties". What happens is if you open the context menu, select "properties", it opens the properties dialog and closes the context menu just fine. But when I close my app, the properties window also closes.
The question is: How do you keep that window open? If you can't, how could the app tell if it has a window/dialog dependent on it open? EnumWindows doesn't show it; Spy++ shows the Properties Dialog stands on its own as well (under desktop).
CComPtr<IContextMenu> pMenu;
CComPtr<IShellFolder> pFolder;
PCUITEMID_CHILD pidl = NULL;
SHBindToParent(item.pidl, IID_IShellFolder, (void**)&pFolder, &pidl);
pFolder->GetUIObjectOf(NULL, 1, &pidl, IID_IContextMenu, NULL, (void**)&pMenu);
UINT flags = CMF_NORMAL | CMF_CANRENAME;
pMenu->QueryContextMenu(menu, CID_FIRST, CID_LAST, 0x7fff, flags);
.
.
.
// command invocation
CMINVOKECOMMANDINFOEX info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = CMIC_MASK_NOASYNC | CMIC_MASK_PTINVOKE; // I've played around with flags
info.hwnd = toolbar.m_hWnd; // I've played around with this value
info.lpVerb = MAKEINTRESOURCEA(cid - CID_LAST);
info.nShow = SW_SHOWNORMAL;
info.ptInvoke = point;
pMenu->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
TIA!!
The properties dialog is in-process.
You need to call SHSetInstanceExplorer and keep your process alive until that COM object is released:
int main()
{
struct CoInit { HRESULT m_hr; CoInit() { m_hr = CoInitialize(0); } ~CoInit() { if (SUCCEEDED(m_hr)) CoUninitialize(); } } coinit;
ProcessReference ref;
IShellItem*pSI;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_Windows, KF_FLAG_DEFAULT, L"Explorer.exe", IID_IShellItem, (void**) &pSI);
if (hr) return hr;
IContextMenu*pCM;
hr = pSI->BindToHandler(NULL, BHID_SFUIObject, IID_IContextMenu, (void**) &pCM);
pSI->Release();
if (hr) return hr;
//HMENU menu = CreatePopupMenu();
//pCM->QueryContextMenu(menu, 0, 1, 0x7fff, CMF_NORMAL);
CMINVOKECOMMANDINFO info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = CMIC_MASK_NOASYNC; // I've played around with flags
info.hwnd = NULL; // I've played around with this value
info.lpVerb = "Properties";
info.nShow = SW_SHOWNORMAL;
//info.ptInvoke = point;
hr = pCM->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
pCM->Release();
return hr;
}
EnumWindows doesn't show it
If I misunderstood something, please tell me.
This is my test:
The second screenshot is what I got with EnumWindows, in my opinion, For some APP Properties Dialog , you can get it with EnumWindows.
At the same time, as #Jonathan Potter said that, the shell properties dialog runs in-process, it is true that you can open the properties dialog independently with ShellExecuteEx.
Only as a supplement, if you just want to open the properties dialog independently, you can use the api above.This way, when you close the app, you can reopen the properties dialog if you are not bothered.
Minimal code example:
#include <windows.h>
#include <shlobj_core.h>
#pragma comment(lib,"Shell32.lib")
class ProcessReference : public IUnknown {
public:
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown) {
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL; return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
LONG lRef = InterlockedDecrement(&m_cRef);
if (lRef == 0) PostThreadMessage(m_dwThread, WM_NULL, 0, 0);
return lRef;
}
ProcessReference()
: m_cRef(1), m_dwThread(GetCurrentThreadId())
{
SHSetInstanceExplorer(this);
}
~ProcessReference()
{
SHSetInstanceExplorer(NULL);
Release();
MSG msg;
while (m_cRef && GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
private:
LONG m_cRef;
DWORD m_dwThread;
};
int main(void)
{
struct CoInit { HRESULT m_hr; CoInit() { m_hr = CoInitialize(0); } ~CoInit() { if (SUCCEEDED(m_hr)) CoUninitialize(); } } coinit;
ProcessReference ref;
IShellItem*pSI;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_Windows, KF_FLAG_DEFAULT, L"Explorer.exe", IID_IShellItem, (void**)&pSI);
if (hr) return hr;
IContextMenu*pCM;
hr = pSI->BindToHandler(NULL, BHID_SFUIObject, IID_IContextMenu, (void**)&pCM);
pSI->Release();
if (hr) return hr;
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof info;
info.lpFile = "C:\\Users\\strives\\Desktop\\print.txt";
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
info.lpVerb = "properties";
ShellExecuteEx(&info);
}
Updated:
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof info;
info.lpFile = "C:\\Users\\strives\\Desktop\\Test.txt";
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
info.lpVerb = "openas";
ShellExecuteEx(&info);
Debug:
By using shell namespace extension, I create a virtual folder and add a "New Folder" button on the toolbar, and I can see the button when I use windows explorer to open it:
The related code about the "New Folder" button is simple:
class CFolderViewCommandProvider : public IExplorerCommandProvider
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CFolderViewCommandProvider, IExplorerCommandProvider),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
{
delete this;
}
return cRef;
}
// IExplorerCommandProvider
IFACEMETHODIMP GetCommands(IUnknown *punkSite, REFIID riid, void **ppv);
IFACEMETHODIMP GetCommand(REFGUID /* rguidCommandId */, REFIID /* riid */, void **ppv)
{ *ppv = NULL; return E_NOTIMPL; }
CFolderViewCommandProvider() : _cRef(1)
{
}
private:
static HRESULT s_NewFolder(IShellItemArray *psiItemArray, IUnknown *pv);
.
.
.
and
const FVCOMMANDITEM CFolderViewCommandProvider::c_FVTasks[] =
{
{ &GUID_Settings, IDS_SETTINGS, IDS_SETTINGS, L"shell32.dll,-16710", 0, CFolderViewCommandProvider::s_NewFolder, NULL, 0 }
};
IFACEMETHODIMP CFolderViewCommandProvider::GetCommands(IUnknown * /* punkSite */, REFIID riid, void ** ppv)
{
*ppv = NULL;
CFolderViewCommandEnumerator *pFVCommandEnum = new (std::nothrow) CFolderViewCommandEnumerator(c_FVTasks, ARRAYSIZE(c_FVTasks));
HRESULT hr = pFVCommandEnum ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pFVCommandEnum->QueryInterface(riid, ppv);
pFVCommandEnum->Release();
}
return S_OK;
}
IFACEMETHODIMP CFolderViewCommand::Invoke(IShellItemArray *psiItemArray, IBindCtx *pbc)
{
HRESULT hr = S_OK; // If no function defined - just return S_OK
if (_pfvci && _pfvci->pfnInvoke)
{
hr = _pfvci->pfnInvoke(psiItemArray, pbc);
}
return hr;
}
.
.
.
Thew "New Folder" button just simply pops up a message box when it is clicked.
But when I use CFileDialog to browse this virtual folder, I cannot find any "New Folder" button in the tool bar:
I can see "New Folder" button for all other system folders like under "Document" or C drive in the CFileDialog, but the "New Folder" button for my virtual folder disappears when I open it in CFileDialog.
How to fix that problem?
I have been following several guides/examples on creating a Vista/7 open file dialog box and have now hit a dead end due to an error message stating that main.cpp(189): error C3861: 'CDialogEventHandler_CreateInstance': identifier not found.
I could find nothing about this on Google except for one page where someone rewrote the sample code to not use the CDialogEventHandler class.
Here is my code. The CDialogEventHandler class and function definitions come straight from the Windows 7 SDK sample for the CommonFileDialogApp:
#include "resource.h"
#include <windows.h> // For common windows data types and function headers
#define STRICT_TYPED_ITEMIDS
#include <objbase.h> // For COM headers
#include <shobjidl.h> // for IFileDialogEvents and IFileDialogControlEvents
#include <shlwapi.h>
#include <knownfolders.h> // for KnownFolder APIs/datatypes/function headers
#include <propvarutil.h> // for PROPVAR-related functions
#include <propkey.h> // for the Property key APIs/datatypes
#include <propidl.h> // for the Property System APIs
#include <strsafe.h> // for StringCchPrintfW
#include <shtypes.h> // for COMDLG_FILTERSPEC
#include <new>
// Controls
#define CONTROL_GROUP 2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1 1
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST,
// because it is a child control under CONTROL_RADIOBUTTONLIST
// IDs for the Task Dialog Buttons
#define IDC_BASICFILEOPEN 100
#define IDC_ADDITEMSTOCUSTOMPLACES 101
#define IDC_ADDCUSTOMCONTROLS 102
#define IDC_SETDEFAULTVALUESFORPROPERTIES 103
#define IDC_WRITEPROPERTIESUSINGHANDLERS 104
#define IDC_WRITEPROPERTIESWITHOUTUSINGHANDLERS 105
HWND ghMainWnd = 0;
HINSTANCE ghAppInst = 0;
RECT winRect;
void centerWnd(HWND parent_window);
void openDB();
class CDialogEventHandler : public IFileDialogEvents,
public IFileDialogControlEvents
{
public:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog *pfd);
IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; };
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem);
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize *, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; };
CDialogEventHandler() : _cRef(1) { };
private:
~CDialogEventHandler() { };
long _cRef;
};
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_OPENDB:
openDB();
break;
default:
break;
}
break;
case WM_LBUTTONDOWN:
MessageBox(0, "WM_LBUTTONDOWN message.", "Msg", MB_OK);
return 0;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(ghMainWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR cmdLine, int showCmd)
{
ghAppInst = hInstance;
HMENU mMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghAppInst;
wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
wc.hCursor = ::LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH) ::GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "MyWndClassName";
RegisterClass(&wc);
centerWnd(GetDesktopWindow());
ghMainWnd = ::CreateWindow("MyWndClassName", "Space Crusade Database Editor V1.0", WS_OVERLAPPEDWINDOW, winRect.left, winRect.top, 1280, 720, 0, mMenu, ghAppInst, 0);
if(ghMainWnd == 0)
{
::MessageBox(0, "CreateWindow - Failed", 0, 0);
return false;
}
ShowWindow(ghMainWnd, showCmd);
UpdateWindow(ghMainWnd);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
void centerWnd(HWND parent_window)
{
GetClientRect(parent_window, &winRect);
winRect.left = (winRect.right/2) - (1280/2);
winRect.top = (winRect.bottom/2) - (720/2);
}
void openDB()
{
//Cocreate the file open dialog object
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
//Stuff needed for later
const COMDLG_FILTERSPEC rgFExt[] = {{L"SQLite3 Database (*.sqlite)", L"*.sqlite"}};
WCHAR fPath[MAX_PATH] = {};
//Create event handling
IFileDialogEvents *pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if(SUCCEEDED(hr))
{
//Hook the event handler
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
//Set options for the dialog
DWORD dwFlags;
//Get options first so we do not override
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
//Get shell items only
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr))
{
//Types of files to display (not default)
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
if (SUCCEEDED(hr))
{
//Set default file type to display
hr = pfd->SetDefaultExtension(L"sqlite");
if (SUCCEEDED(hr))
{
//Show dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
//Get the result once the user clicks on open
IShellItem *result;
hr = pfd->GetResult(&result);
if (SUCCEEDED(hr))
{
//Print out the file name
PWSTR fName = NULL;
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
if (SUCCEEDED(hr))
{
TaskDialog(NULL,NULL,L"File Name",fName,NULL,TDCBF_OK_BUTTON,TD_INFORMATION_ICON,NULL);
CoTaskMemFree(fName);
}
result->Release();
}
}
}
}
}
}
}
pfd->Unadvise(dwCookie);
}
pfde->Release();
}
pfd->Release();
}
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CDialogEventHandler *pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog *pfd)
{
IFileSaveDialog *pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr))
{
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr))
{
IPropertyDescriptionList *pdl = NULL;
}
pfsd->Release();
}
return hr;
}
// IFileDialogControlEvents
// This method gets called when an dialog control item selection happens (radio-button selection. etc).
// For sample sake, let's react to this event by changing the dialog title.
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem)
{
IFileDialog *pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr))
{
if (dwIDCtl == CONTROL_RADIOBUTTONLIST)
{
switch (dwIDItem)
{
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
pfd->Release();
}
return hr;
}
So I managed to get the code to work. What I did was I took the solution file from the Sample mentioned in the original post and attempted to build and run it. This was successful which lead me to believe that the problem was in the project settings. I tried switching back to Unicode for the typeset but this did not fix the problem.
In the end I just co-opted the Sample project and I am now altering it to me needs. I am not sure what project options are needed to make it work but I am almost sure it has to do with with something in the linker options.
Not sure you have got the entire sample code, try github sample below with these libraries, should compile first time.
//Libraries I added to get this to work.
#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"propsys.lib")
#pragma comment(lib,"shlwapi.lib")
https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/winui/shell/appplatform/commonfiledialog/CommonFileDialogApp.cpp
CDialogEventHandler_CreateInstance is a helper function defined in Microsoft's CommonFileDialogue example app. You can also find the GitHub page from the MSDN page.
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CDialogEventHandler *pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
You used function prototypes (I believe that is what they are called, forward declarations of functions before the main function) for centerWnd and openDB, but not the CDialogEventHandler_CreateInstance function. Try adding
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv);
under
void openDB();