SHBrowseForFolderA Errors - c++

I recently found some code on here which shows us how to let users choose the directory in C++ which isn't as simple as C#. I keep getting three errors I barley understand. And i want to know how to store the path onto a string.
ERRORS:
argument of type "char *" is incompatible with parameter of type "LPWSTR"
a value of type "const char *" cannot be assigned to an entity of type "LPCWSTR"
no suitable constructor exists to convert from "TCHAR [260]" to "std::basic_string<char, std::char_traits<char>, std::allocator<char>>"
and here is the code i'm using:
int CALLBACK BrowseForFolderCallback(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
{
char szPath[MAX_PATH];
switch (uMsg)
{
case BFFM_INITIALIZED:
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData);
break;
case BFFM_SELCHANGED:
if (SHGetPathFromIDList((LPITEMIDLIST)lp, szPath))
{
SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szPath);
}
break;
}
return 0;
}
static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED)
{
std::string tmp = (const char*)lpData;
std::cout << "path: " << tmp << std::endl;
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
}
return 0;
}
std::string BrowseFolder(std::string saved_path)
{
TCHAR path[MAX_PATH];
const char* path_param = saved_path.c_str();
BROWSEINFO bi = { 0 };
bi.lpszTitle = ("Browse for folder...");
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)path_param;
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
if (pidl != 0)
{
//get the name of the folder and put it in path
SHGetPathFromIDList(pidl, path);
//free memory used
IMalloc* imalloc = 0;
if (SUCCEEDED(SHGetMalloc(&imalloc)))
{
imalloc->Free(pidl);
imalloc->Release();
}
return path;
}
return "";
}
The First Error occurs in: case BFFM_SELCHANGED
The Second Error occurs in: bi.lpszTitle = ("Browse for folder...");
The Final Error occurs in: return path;
EDIT: The second and third error are fixed. However, the first one still remains and i have no clue how to fix it

try to add a L in front of the strings, like that :
bi.lpszTitle = (L"Browse for folder...");
this will turn the string into a Wide String

in order to make this code compile with Visual Studio :
Project -> Properties -> Advanced -> Character Set = Use Multi-Byte ChararacterSet

Related

How can I show a window with the a name given as parameter in Windows in C++

I would like to make my software usable for Linux and Windows (Linux already works). Now I still need some functions so I can run the software on Windows, too.
I am currently trying to use the EnumWindows() function to get the window names and then show the window in the foreground (which matches the parameter).
static BOOL CALLBACK setWindowFocus(HWND hWnd, LPARAM lparam) {
int length = GetWindowTextLength(hWnd);
char* buffer = new char[length + 1];
GetWindowText(hWnd, buffer, length + 1);
std::string windowTitle(buffer);
// List visible windows with a non-empty title
if (IsWindowVisible(hWnd) && length != 0) {
// Check if it is the right Windowshandle
if ( windowTitle.compare(programname) == 0 ) <-- programname is a static variable
{
// Set application to the foreground
SetForegroundWindow(hWnd);
}
}
return TRUE;
}
Additionally, I used this to create the variable:
std::string programname;
And this to call it:
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
tempsavedProgramname=programname;
EnumWindows(setWindowFocus, NULL);
}
That is working, as long as it is in main(). But, I would like to have it in an extra class with some other functions (I would like to remove the static variable too, if possible).
Is there a way I can use the EnumWindows() function with an anonymous function, or something?
Can I use something like this to pass a string to the function:
EnumWindows(setWindowFocus, reinterpret_cast<LPARAM>(stringvariable));
Or, are there other ways which I can try to reach my goal?
Includes which I used for the code:
Windows.h
winuser.h
string
iostream
I hope that I did not forgot one.
Yes, you can use the LPARAM to pass a string variable into your callback, eg:
static BOOL CALLBACK setWindowFocus(HWND hWnd, LPARAM lparam) {
std::string &programname = *reinterpret_cast<std::string*>(lparam);
int length = GetWindowTextLength(hWnd);
char* buffer = new char[length + 1];
GetWindowText(hWnd, buffer, length + 1);
std::string windowTitle(buffer);
delete[] buffer; // <-- ADD THIS!
/* I would use this instead:
int length = GetWindowTextLength(hWnd);
std::string windowTitle(length+1, '\0');
windowTitle.resize(GetWindowText(hWnd, &windowTitle[0], length + 1));
*/
// List visible windows with a non-empty title
if (IsWindowVisible(hWnd) && (length != 0)) {
// Check if it is the right Windowshandle
if (windowTitle == programname)
{
// Set application to the foreground
SetForegroundWindow(hWnd);
return FALSE;
}
}
return TRUE;
}
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
EnumWindows(setWindowFocus, reinterpret_cast<LPARAM>(&programname));
}
And yes, you can use a C++11 lambda for the callback, rather than a static class method, but only if you use a non-capturing lambda, which is implicitly convertible to a function pointer (capturing lambdas are not). Fortunately, the LPARAM makes that a possibility, eg:
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
EnumWindows(
[](HWND hWnd, LPARAM lparam) -> BOOL {
std::string &programname = *reinterpret_cast<std::string*>(lparam);
// ...
},
reinterpret_cast<LPARAM>(&programname)
);
}
Now, that being said, there is a much simpler solution - since you already know the exact window text you are looking for, you can use FindWindow() instead of EnumWindows(), eg:
static void setWindowFocus(std::string programname)
{
std::cout << "Setting focus to window." << std::endl;
HWND hWnd = FindWindowA(NULL, programname.c_str());
if (hWnd != NULL) {
// Set application to the foreground
SetForegroundWindow(hWnd);
}
}
Here's the callback wrapped up in a class
class enum_windows {
protected:
virtual BOOL call_back(HWND hwnd) {
// Your code here
return TRUE;
}
public:
void start() {
EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
enum_windows * obj = reinterpret_cast<enum_windows *>(lParam);
return obj->call_back(hwnd);
}, reinterpret_cast<LPARAM>(this));
}
};
(You've already accepted an answer - I'm a slow typer 😄). I'll leave this here anyway.

How do I add a property page to CPrintDialogEx

I'm trying to implement CPrintDialogEx. I have some additional needed options and I want to add another property page to the window. There are no MFC examples and trying the Win API example fails miserably. It cashes. What am I doing wrong?
CPrintDialogEx dlg;
PROPSHEETPAGE optionsPage1;
HPROPSHEETPAGE hOptionsPage;
memset(&optionsPage1, 0, sizeof(PROPSHEETPAGE));
optionsPage1.dwSize = sizeof(PROPSHEETPAGE);
optionsPage1.dwFlags = PSP_DLGINDIRECT;
optionsPage1.hInstance = AfxGetInstanceHandle();
optionsPage1.pszTemplate = MAKEINTRESOURCE(IDD_QREPORT_OPTIONS);
optionsPage1.pResource = (DLGTEMPLATE*)IDD_QREPORT_OPTIONS;
optionsPage1.hIcon = NULL;
optionsPage1.pszIcon = NULL;
optionsPage1.pszTitle = "Options";
optionsPage1.pfnDlgProc = AfxWndProc;
optionsPage1.lParam = NULL;
dlg.m_pdex.nPropertyPages = 1;
hOptionsPage = CreatePropertySheetPage(&optionsPage1);
dlg.m_pdex.lphPropertyPages = &hOptionsPage;
if (dlg.DoModal() == IDOK)
NULL pointer crash
optionsPage1.dwFlags = PSP_DLGINDIRECT;
...
optionsPage1.pResource = (DLGTEMPLATE*)IDD_QREPORT_OPTIONS;
IDD_QREPORT_OPTIONS is an integer, it should not be forced to cast in to DLGTEMPLATE*. Doing so will point pResource to some random memory address and is likely the cause of crash.
You don't need pResource anyway. Replace PSP_DLGINDIRECT with PSP_DEFAULT, this will instruct CreatePropertySheetPage to use pszTemplate.
PROPSHEETPAGE documentation:
pszTemplate
Type: LPCSTR
Dialog box template to use to create the page. This member can specify
either the resource identifier of the template or the address of a
string that specifies the name of the template. If the PSP_DLGINDIRECT
flag in the dwFlags member is set, pszTemplate is ignored. This member
is declared as a union with pResource.
Example:
INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_COMMAND:
if(LOWORD(wParam) == IDC_BUTTON1)
MessageBox(hwnd, _T("test"), 0, 0);
return 0;
}
return FALSE;
}
PROPSHEETPAGE optionsPage1 = { 0 };
optionsPage1.dwSize = sizeof(PROPSHEETPAGE);
optionsPage1.dwFlags = PSP_DEFAULT | PSP_USETITLE;
optionsPage1.hInstance = AfxGetInstanceHandle();
optionsPage1.pszTemplate = MAKEINTRESOURCE(IDD_QREPORT_OPTIONS);
optionsPage1.pszTitle = _T("Options");
optionsPage1.pfnDlgProc = dlgproc;// AfxWndProc;

How to find the names of all files from the clipboard

I just created an MFC application to find the file names from the clip board
AddClipboardFormatListener(AfxGetApp()->m_pMainWnd->m_hWnd);
LRESULT Cfile_trackerDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CLIPBOARDUPDATE:
{
AfxBeginThread(FileArrival, NULL);
break;
}
case WM_CHANGECBCHAIN:
{
AfxBeginThread(FileArrival, NULL);
break;
}
}
return CDialog::WindowProc(message, wParam, lParam);
}
UINT FileArrival(LPVOID param)
{
TCHAR lpszFileName[MAX_PATH];
char *szTime;
time_t thistime;
OpenClipboard(0);
HGLOBAL hGlobal = (HGLOBAL)GetClipboardData(CF_HDROP);
if (hGlobal)
{
HDROP hDrop = (HDROP)GlobalLock(hGlobal);
if (hDrop)
{
time(&thistime);
szTime = ctime(&thistime);
DragQueryFile(hDrop, 0, lpszFileName, MAX_PATH);
WriteLog((char*)lpszFileName,1);
GlobalUnlock(hGlobal);
}
CloseClipboard();
}
return 0;
}
This code is working fine when we copy 1 file , but when we copy multiple files it only shows the first file. Is there is any method to find out all the file names that copied to the clipboard.
You use this to find the number of files that were dropped:
UINT fileCount = DragQueryFile(hDrop, 0xFFFFFFFF, nullptr, 0);
With this information, you can allocate an array of strings and store each filename into a string in the array:
TCHAR** filenames;
// other code . . .
filenames = malloc(fileCount * sizeof(TCHAR*));
// other code . . .
for (UINT i = 0; i < fileCount; ++i) {
UINT filenameLength = DragQueryFile(hDrop, i, nullptr, 0);
filenames[i] = malloc(filenameLength);
DragQueryFile(hDrop, i, filenames[i], filenameLength);
}
I figured this all out from reading the documentation.
EDIT: To use the C++ standard library here (prevents having to manually free allocated memory), you can use the following code:
std::vector<std::basic_string<TCHAR> > filenames(fileCount);
// other code . . .
for (UINT i = 0; i < fileCount; ++i) {
UINT filenameLength = DragQueryFile(hDrop, i, nullptr, 0);
filenames[i].reserve(filenameLength);
DragQueryFile(hDrop, i, &(filenames[i][0]), filenameLength);
// Uncomment the below line and comment the above line if you can use C++17 features
// DragQueryFile(hDrop, i, filenames[i].data(), filenameLength);
}

Trying to get text from tooltips not working

I've been trying for a couple of hours to interrogate tooltips to give up the text they contain to no avail. I've found How to get tooltip text for a given HWND? and tried that without success.
This shouldn't be that hard. I'm just not sure what I'm doing wrong. Here's a section of my code:
BOOL CALLBACK EnumWindowsProc(
_In_ HWND hwnd,
_In_ LPARAM lParam
)
{
TCHAR className[200];
GetClassName(hwnd, className, _countof(className));
ASSERT(IsWindow(hwnd));
if (_tcscmp(className, _T("tooltips_class32")) == 0)
{
TOOLINFO ti = { 0 };
ti.cbSize = sizeof(TOOLINFO);
TCHAR text[500] = { 0 };
ti.lpszText = text;
ti.hwnd = GetParent(hwnd);
IsWindow(ti.hwnd);
ti.uId = GetDlgCtrlID(hwnd);
int result = SendMessage(hwnd, TTM_GETTEXT, _countof(text), (LPARAM)&ti);
CString info;
info.Format(_T("%p: %s \"%s\"\r\n"), hwnd, className, ti.lpszText);
CString& output = *(CString*)lParam;
output += info;
}
return 1;
}
void CTooltipVerifyDlg::OnTimer(UINT_PTR nIDEvent)
{
m_output = "";
VERIFY(EnumWindows(EnumWindowsProc, (LPARAM)&m_output));
SYSTEMTIME systemTime;
GetLocalTime(&systemTime);
CString text;
text.Format(_T("%02u:%02u:%02u.%03u\r\n"), systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
m_output = text + m_output;
this->UpdateData(FALSE);
CDialogEx::OnTimer(nIDEvent);
}
CTooltipVerifyDlg is a dialogue with a text box which I communicate to with m_output, a CString that is bound to the text box.
When the SendMessage call is done, something on my desktop (or even the desktop manager) crashes. Any ideas why it would be crashing and not giving me the text that I desire?

Win32 - Select Directory Dialog from C/C++

How to select an existing folder (or create new) from a native Win32 application?
Here is a similar question. It has a good answer for C#/.NET. But I want the same thing for native Win32.
Anybody knows a solution, free code, etc?
Update:
I tried the function from the answer. Everything worked as expected, except it is necessary to call the SHGetPathFromIDList function to retrieve the name of selected directory. Here is a sample screen shot:
SHBrowseForFolder
Do your users a favor, and set at least the BIF_NEWDIALOGSTYLE flag.
To set the initial folder, add the following code:
static int CALLBACK BrowseFolderCallback(
HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED) {
LPCTSTR path = reinterpret_cast<LPCTSTR>(lpData);
::SendMessage(hwnd, BFFM_SETSELECTION, true, (LPARAM) path);
}
return 0;
}
// ...
BROWSEINFO binf = { 0 };
...
binf.lParam = reinterpret_cast<LPARAM>(initial_path_as_lpctstr);
binf.lpfn = BrowseFolderCallback;
...
and provide a suitable path (such as remembering the last selection, your applications data folder, or similar)
Just as a go to for future users, this article helped me a lot with getting a directory dialog in C++
http://www.codeproject.com/Articles/2604/Browse-Folder-dialog-search-folder-and-all-sub-fol
Here is my code (heavily based/taken on the article)
NOTE: You should be able to copy/paste this into a file / compile it (g++, see VS in ninja edit below) and it'll work.
#include <windows.h>
#include <string>
#include <shlobj.h>
#include <iostream>
#include <sstream>
static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if(uMsg == BFFM_INITIALIZED)
{
std::string tmp = (const char *) lpData;
std::cout << "path: " << tmp << std::endl;
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
}
return 0;
}
std::string BrowseFolder(std::string saved_path)
{
TCHAR path[MAX_PATH];
const char * path_param = saved_path.c_str();
BROWSEINFO bi = { 0 };
bi.lpszTitle = ("Browse for folder...");
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM) path_param;
LPITEMIDLIST pidl = SHBrowseForFolder ( &bi );
if ( pidl != 0 )
{
//get the name of the folder and put it in path
SHGetPathFromIDList ( pidl, path );
//free memory used
IMalloc * imalloc = 0;
if ( SUCCEEDED( SHGetMalloc ( &imalloc )) )
{
imalloc->Free ( pidl );
imalloc->Release ( );
}
return path;
}
return "";
}
int main(int argc, const char *argv[])
{
std::string path = BrowseFolder(argv[1]);
std::cout << path << std::endl;
return 0;
}
EDIT: I've updated the code to show people how to remember the last selected path and use that.
Also, for VS, using Unicode character set. replace this line:
const char * path_param = saved_path.c_str();
With this:
std::wstring wsaved_path(saved_path.begin(),saved_path.end());
const wchar_t * path_param = wsaved_path.c_str();
My Test code above is compiled with g++, but doing this fixed it in VS for me.
For Windows Vista and above, it's best to use IFileOpenDialog with the FOS_PICKFOLDERS option for a proper open dialog rather than this tree dialog. See Common Item Dialog on MSDN for more details.