C++/MFC, Windows 11. Visual Studio 2022 17.4.3.
I'm using CFileDialog to allow user to chose a file. By creating a new class derived from CFileDialog, I am being notified whenever the user changes directories (folders).
I implemented this so I could control the filter applied to the list of files in the directory. However, I have been unsuccessful in this. Even if I don't change m_ofn, I get an error.
Here is some sample code:
// Caller
#include "Browsing_test.h"
P brTest(true, NULL, NULL, 0, fileTypes);
brTest.BrowseTest();
// Browsing_test.h
class P : CFileDialog
{
public:
P(BOOL bOpenFileDialog,
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd *pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = TRUE) : CFileDialog(bOpenFileDialog,
lpszDefExt,
lpszFileName,
dwFlags,
lpszFilter,
pParentWnd,
dwSize,
bVistaStyle) {};
int BrowseTest(void);
#include "stdafx.h"
#include "Browsing_test.h"
int P::BrowseTest(void)
{
int resultDebug = (int)DoModal();
return resultDebug;
}
void P::OnFolderChange()
{
auto s = GetOFN(); // for modifying m_ofn member of the base class,
// but not used in this sample code
// Add modificatons to m_ofn here
ApplyOFNToShellDialog(); // Gets assert on updating flags
}
Running this code gives an error in dlgfile.cpp (Microsoft code) at line:
hr = (static_cast<IFileDialog*>(m_pIFileDialog))->SetOptions(dwFlags);
which returns hr = E_UNEXPECTED Catastrophic failure. The value of dwFlags was hex 40.
Related
I need to implement a wrapper class around Winreg.h library.
Problem: All functions work correctly except RegCreateKeyEx, when I try to pass the parameters it returns Error 87.
Example:
When I hardcode the subkey inside the function it works just fine.
HKEY hKey = NULL;
DWORD dc;
int rc = RegCreateKeyEx(HKEY_USERS, L"AppUser\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dc);
But when I parameterized the function like this so I can make the subkey variable and make resursive calls to it it produces Error 87.
HKEY hKey = NULL;
DWORD dc;
const std::wstring szSub = L"AppUser\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer";
LPCWSTR lpszSub = szSub.c_str();
int rc = RegCreateKeyEx(HKEY_USERS, lpszSub, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dc);
I'm just starting with this library so I don't know what I'm missing, maybe a formatting error or something else. I would appreciate any help or suggestion.
I am working on a legacy application which calls CreateProcess method
method1()
{
BOOL bResult = ::CreateProcess(
NULL, // LPCTSTR
wctAppPath, // LPTSTR lpszCommandLine
NULL, // LPSECURITY_ATTRIBUTES lpsaProc
NULL, // LPSECURITY_ATTRIBUTES lpsaThread
FALSE, // BOOL fInheritHandles
NULL, // DWORD fdwCreate
NULL, // LPVOID lpvEnvironment
NULL, // LPCTSTR lpszCurDir
&stStatusInfo, // LPSTARTUPINFO lpsiStartInfo
&stProcInfo // LPPROCESS_INFORMATION lppiProcInfo
);
}
wctAppPath value : (folderPath\test.bat ..\excel.xls)
this CreateProcess trigger bat file that will launch vbscript and passed excel file is modified.
Once this method1() ends, they are calling CreateProcess() again in method2() to open the same excel file.
method2()
{
BOOL bResult = ::CreateProcess(
NULL, // LPCTSTR
wctAppPath, // LPTSTR
NULL, // LPSECURITY_ATTRIBUTES lpsaProc
NULL, // LPSECURITY_ATTRIBUTES lpsaThread
FALSE, // BOOL fInheritHandles
NULL, // DWORD fdwCreate
NULL, // LPVOID lpvEnvironment
NULL, // LPCTSTR lpszCurDir
&stStatusInfo, // LPSTARTUPINFO lpsiStartInfo
&stProcInfo // LPPROCESS_INFORMATION lppiProcInfo
);
}
Here wctAppPath is (excelInstallationPath\EXCEL.exe folderPath\excelFile.xls)
Once all the main function ends, the required modified excel in stored in Documents folder not in the actual given path. Actual given excel is not modified at all.
I have commented the method2 and tried. Expected excel appears in given path.
Legacy application is CATIA.
In V5, CATScriptUtilities::ExecuteScript is used to call catvba.
In V6, we can use the same API to call catvba. It works fine.
There is no need to call .bat->.vba->.catvba
I was trying to wrap CFileDialog and IFileOpenDialog in a class, here is the code:
class ITest
{
public:
virtual ~ITest(){};
virtual INT_PTR DoModal() = 0;
virtual IFileOpenDialog* GetDlg() = 0;
};
class test : public ITest
{
public:
test(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = FALSE)
{
dlg = new CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, dwSize, bVistaStyle);
};
~test(){};
INT_PTR DoModal(){ return S_OK; };
IFileOpenDialog* GetDlg(){ return dlg->GetIFileOpenDialog(); };
private:
CFileDialog* dlg;
};
Then when I call it like:
ITest* a = new test(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this);
fdlg = a->GetDlg();
fdlg->Show(NULL);//it crashes here since fdlg is NULL
Then I tried to call CFileDialog and IFileOpenDialog directly:
IFileOpenDialog* fdlg = NULL;
CFileDialog* b = new CFileDialog(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this);
fdlg = b->GetIFileOpenDialog();
fdlg->Show(NULL);
This part of code works perfectly.
Anyone knows why the crash happens ?
It confuses me so much since I feel they are so similar.
class test : public ITest
{
public:
test(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = FALSE)
{
dlg = new CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, dwSize, bVistaStyle);
};
~test(){};
INT_PTR DoModal(){ return S_OK; };
IFileOpenDialog* GetDlg();
private:
CFileDialog* dlg;
};
IFileOpenDialog* test::GetDlg()
{
return dlg->GetIFileOpenDialog();
};
The above code also doesn't work( it defines IFileOpenDialog* GetDlg(); outside of the class declartion)
From the help:
Use this function only under Windows Vista with an object that has
bVistaStyle set to true. This function returns NULL if the CFileDialog
is not an Open dialog box or if bVistaStyle is set to false.
When you construct your test object:
ITest* a = new test(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this);
you are leaving our the last parameter of your constructor, bVistaStyle, which defaults to FALSE. So just add it:
ITest* a = new test(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this, 0,
TRUE); // bVistaStyle
-------------------------update 5th-------------------------------
if (fIsFolder)
{
dwAttribs |= SFGAO_FOLDER;
}
else
{
dwAttribs |= SFGAO_SYSTEM;
dwAttribs |= SFGAO_FILESYSTEM;
}
if (nLevel < g_nMaxLevel)
{
dwAttribs |= SFGAO_HASSUBFOLDER;
dwAttribs |= SFGAO_FILESYSANCESTOR;
}
Now this one works normally in application A, stackoverflow image upload file dialog and in regular folder explorer.
-----------------------------------------------------update 4th-----------------------------------------------------------
After add dwAttribs |= SFGAO_SYSTEM; , now at least for application A, it works as expected in the file dialog. But the folder is still not clickable in the regular folder dialog or for CWFileDialog.
if (fIsFolder)
{
dwAttribs |= SFGAO_FOLDER;
dwAttribs |= SFGAO_FILESYSTEM;
dwAttribs |= SFGAO_FILESYSANCESTOR;
}
else
{
dwAttribs |= SFGAO_SYSTEM;// this line of code works better
dwAttribs |= SFGAO_FILESYSTEM;
}
-----------------------------------update third-----------------------------------------
This is the code in the virtual folder's ISHellFolder:
After I edit the following code to add dwAttribs |= SFGAO_FILESYSTEM;, the folder cannot be browsed when I double click it in the folder view. But it can be opened by left clicking it in the tree view.
HRESULT CFolderViewImplFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, ULONG *rgfInOut)
{
// If SFGAO_FILESYSTEM is returned, GetDisplayNameOf(SHGDN_FORPARSING) on that item MUST
// return a filesystem path.
HRESULT hr = E_INVALIDARG;
DWORD dwAttribs = 0;
dwAttribs |= SFGAO_FILESYSTEM;
if (1 == cidl)
{
int nLevel = 0;
hr = _GetLevel(apidl[0], &nLevel);
if (SUCCEEDED(hr))
{
BOOL fIsFolder = FALSE;
hr = _GetFolderness(apidl[0], &fIsFolder);
if (SUCCEEDED(hr))
{
if (fIsFolder)
{
dwAttribs |= SFGAO_FOLDER;
}
if (nLevel < g_nMaxLevel)
{
dwAttribs |= SFGAO_HASSUBFOLDER;
}
}
}
}
*rgfInOut &= dwAttribs;
return hr;
}`
Or
DWORD dwAttribs = 0;
if (1 == cidl)
{
int nLevel = 0;
hr = _GetLevel(apidl[0], &nLevel);
if (SUCCEEDED(hr))
{
BOOL fIsFolder = FALSE;
hr = _GetFolderness(apidl[0], &fIsFolder);
if (SUCCEEDED(hr))
{
if (fIsFolder)
{
dwAttribs |= SFGAO_FOLDER;
dwAttribs |= SFGAO_FILESYSTEM;
dwAttribs |= SFGAO_FILESYSANCESTOR;
}
else
{
dwAttribs |= SFGAO_SYSTEM;
}
if (nLevel < g_nMaxLevel)
{
dwAttribs |= SFGAO_HASSUBFOLDER;
}
}
}
}
*rgfInOut &= dwAttribs;
Other code are the same as in update 2nd.
The weird stuff is, after I edit these code, application A which uses CFileDialog normally( not wrapping it, not set m_bPickNonFileSysFoldersMode to true) can display all virtual folder normally as I expected.
But in all other applications( including the one uses CWFileDilao which set m_bPickNonFileSysFoldersMode to true is still cannot view the virtual folder.
---------------------------------------update 2nd-------------------------
I code a simple class which derives from CFileDialog:
class CWFileDlg : public CFileDialog
{
public:
CWFileDlg(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = TRUE);
~CWFileDlg();
};
CWFileDlg::CWFileDlg(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt,
LPCTSTR lpszFileName,
DWORD dwFlags,
LPCTSTR lpszFilter,
CWnd* pParentWnd,
DWORD dwSize,
BOOL bVistaStyle) : CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, dwSize, bVistaStyle)
{
m_bPickNonFileSysFoldersMode = TRUE;
}
CWFileDlg::~CWFileDlg()
{
}
Then I call it like:
CWFileDlg dlg(TRUE, NULL, 0, OFN_SHAREAWARE | OFN_ENABLESIZING | OFN_ALLOWMULTISELECT, L"all(*.*)|*.*||", this);
BOOL IsPickNonFileSysFoldersMode = dlg.IsPickNonFileSysFoldersMode();
//the value it gets is 1, which is TRUE
INT_PTR result = dlg.DoModal();
But in the pop up dialog, virtual folder is stil invisible. Tree view for it is also invisible.
------------------------------------------updates----------------------------
protected:
BOOL m_bVistaStyle;
BOOL m_bPickFoldersMode;
BOOL m_bPickNonFileSysFoldersMode;
DWORD m_dwCookie;
void* m_pIFileDialog;
void* m_pIFileDialogCustomize;
The m_bPickNonFileSysFoldersMode is not a public type. How could I set it to TRUE ?
Error 1 error C2248: 'CFileDialog::m_bPickNonFileSysFoldersMode' : cannot access protected member declared in class 'CFileDialog' c:\users\liyuan.liu\documents\dp-dll\testvirtualfolder\browser2\browser2\browser2dlg.cpp 166 1 browser2
By using Microsoft windows sample code, I installed a virtual folder on windows 7 by using shell namespace extension.
The following screenshot is using regular folder browser to open it:
Then I wrote a project which can create a file dialog:
CFileDialog dlg(TRUE, NULL, 0, OFN_SHAREAWARE | OFN_ENABLESIZING | OFN_ALLOWMULTISELECT, L"all(*.*)|*.*||", this);
INT_PTR result = dlg.DoModal();
However, in the file browser, the virtual folder is invisible:
But! When I was trying to upload the screenshot to StackOverflow ( the web browser I am using is chrome), the file dialog which is used to select file can display the tree view ( only the tree view) of the virtual folder:
By googling, it seems that the CFileDialog cannot support to display the virtual folder since it doesn't actually exist in the system. Any solutions to solve that?
I also tried with
bi.pidlRoot = pidlVirtual;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS | BIF_BROWSEINCLUDEFILES | BIF_BROWSEFILEJUNCTIONS;
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
Also doesn't work.
Unfortunately, what the file dialog shows is function of the app that opens it. Starting with Vista, it's the IFileDialog interface which is used, and it defines a set of options through the IFileDialog::SetOptions method.
The FOS_FORCEFILESYSTEM flag ensures that returned items are file system items (SFGAO_FILESYSTEM), and that depends on how you wrote your extension. SFGAO_FILESYSTEM should ring a bell, it's one of the flags the IShellFolder::GetAttributesOf method can return. If what you do is really virtual (ie: if you don't return this flag), they won't be shown when IFileDialog is configured like this.
If you test your extension with different apps (notepad, word, excel, browsers, etc.), you'll see that you see it sometimes, and sometimes you don't.
Spelunking into MFC's code (dlgfile.cpp), you will find this:
// We only expect and handle file system paths (for compatibility with GetOpenFileName functionality), so set the
// "force file system" flag which enables GetOpenFileName-like download behavior for non file system paths, unless
// the m_bPickNonFileSysFoldersMode is set to allow picking non-file system folders (like libraries in Windows 7).
dwFlags |= FOS_FORCEFILESYSTEM;
if (m_bPickNonFileSysFoldersMode)
{
dwFlags &= ~FOS_FORCEFILESYSTEM;
}
So, you need to set m_bPickNonFileSysFoldersMode to TRUE for CFileDialog to show your extension. in MFC you must derive from CFileDialog since this member is protected (BTW this is a stupid MFC design decision), for example::
class MyFileDialog : public CFileDialog
{
public:
MyFileDialog(LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL);
};
MyFileDialog::MyFileDialog(LPCTSTR lpszDefExt, LPCTSTR lpszFileName, DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) : CFileDialog(TRUE, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd)
{
m_bPickNonFileSysFoldersMode = TRUE;
}
Last thing to remember: make sure your virtual folder (namespace extension, etc.) is registered with the same bitness (x86 vs x64) as the app using the CFileDialog.
But it won't fix other apps you don't own that use this flag...
I want to find target path of a shortcut in startmenu folder ,
I know that should use from shell link component object model ,
But in my test for some shortcuts it shows: "windows\installer\{guid}\x.exe" and does not show program files folder for it , and for other shortcut works fine,
How can i find target path for these products.
this is the function i use:
HRESULT TargetShortcut::ResolveIt(HWND hwnd, LPCTSTR lpszLinkFile, LPTSTR lpszPath, int iPathBufferSize)
{
HRESULT hres;
if (lpszPath == NULL)
return E_INVALIDARG;
*lpszPath = 0;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
IShellLink* __psl = NULL;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
// Get a pointer to the IPersistFile interface.
IPersistFile* ppf = NULL;
hres = __psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
if (SUCCEEDED(hres))
{
// Add code here to check return value from MultiByteWideChar
// for success.
// Load the shortcut.
#ifdef _UNICODE
hres = ppf->Load(lpszLinkFile, STGM_READ);
#else
WCHAR wsz[MAX_PATH] = {0};
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, wsz, MAX_PATH);
hres = ppf->Load(wsz, STGM_READ);
#endif
if (SUCCEEDED(hres))
{
// Resolve the link.
hres = __psl->Resolve(hwnd, 0);
if (SUCCEEDED(hres))
{
// Get the path to the link target.
TCHAR szGotPath[MAX_PATH] = {0};
hres = __psl->GetPath(szGotPath, _countof(szGotPath), NULL, SLGP_SHORTPATH);
if (SUCCEEDED(hres))
{
hres = StringCbCopy(lpszPath, iPathBufferSize, szGotPath);
}
}
}
// Release the pointer to the IPersistFile interface.
ppf->Release();
}
// Release the pointer to the IShellLink interface.
__psl->Release();
}
return hres;
}
and this an answer for a shortcut :
C:\Windows\Installer{53FA9A9F-3C19-4D43-AD6B-DEF365D469BA}
Try first this code:
#include "msi.h"
#pragma comment (lib, "msi")
...
TCHAR Path[MAX_PATH];
Path[0] = '\0';
TCHAR pszComponentCode[MAX_FEATURE_CHARS+1];
TCHAR pszProductCode[MAX_FEATURE_CHARS+1];
pszComponentCode[0] = _T('\0');
pszProductCode[0] = _T('\0');
if ( MsiGetShortcutTarget(pszLinkPathName, pszProductCode, NULL, pszComponentCode) == ERROR_SUCCESS)
{
DWORD dw = MAX_PATH;
UINT ret = MsiGetComponentPath(pszProductCode, pszComponentCode, Path, &dw);
//Path now contains path to EXE
}
else
{
//process regular LNK
}
Then in ELSE part you can call code to resolve regular LNK