Last active
February 29, 2024 06:25
-
-
Save 0xF5T9/3f3203950f480d348aa6d99850a26016 to your computer and use it in GitHub Desktop.
IFileOpenDialog, IFileSaveDialog examples.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <windows.h> | |
#include <shobjidl.h> | |
#include <string> | |
#include <vector> | |
/** | |
* @brief Open a dialog to select item(s) or folder(s). | |
* @param paths Specifies the reference to the string vector that will receive the file or folder path(s). [IN] | |
* @param selectFolder Specifies whether to select folder(s) rather than file(s). (optional) | |
* @param multiSelect Specifies whether to allow the user to select multiple items. (optional) | |
* @note If no item(s) were selected, the function still returns true, and the given vector is unmodified. | |
* @note `<windows.h>`, `<string>`, `<vector>`, `<shobjidl.h>` | |
* @return Returns true if all the operations are successfully performed, false otherwise. | |
*/ | |
bool OpenFileDialog(std::vector<std::wstring> &paths, bool selectFolder = false, bool multiSelect = false) | |
{ | |
IFileOpenDialog *p_file_open = nullptr; | |
bool are_all_operation_success = false; | |
while (!are_all_operation_success) | |
{ | |
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, | |
IID_IFileOpenDialog, reinterpret_cast<void **>(&p_file_open)); | |
if (FAILED(hr)) | |
break; | |
if (selectFolder || multiSelect) | |
{ | |
FILEOPENDIALOGOPTIONS options = 0; | |
hr = p_file_open->GetOptions(&options); | |
if (FAILED(hr)) | |
break; | |
if (selectFolder) | |
options |= FOS_PICKFOLDERS; | |
if (multiSelect) | |
options |= FOS_ALLOWMULTISELECT; | |
hr = p_file_open->SetOptions(options); | |
if (FAILED(hr)) | |
break; | |
} | |
hr = p_file_open->Show(NULL); | |
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) // No items were selected. | |
{ | |
are_all_operation_success = true; | |
break; | |
} | |
else if (FAILED(hr)) | |
break; | |
IShellItemArray *p_items; | |
hr = p_file_open->GetResults(&p_items); | |
if (FAILED(hr)) | |
break; | |
DWORD total_items = 0; | |
hr = p_items->GetCount(&total_items); | |
if (FAILED(hr)) | |
break; | |
for (int i = 0; i < static_cast<int>(total_items); ++i) | |
{ | |
IShellItem *p_item; | |
p_items->GetItemAt(i, &p_item); | |
if (SUCCEEDED(hr)) | |
{ | |
PWSTR path; | |
hr = p_item->GetDisplayName(SIGDN_FILESYSPATH, &path); | |
if (SUCCEEDED(hr)) | |
{ | |
paths.push_back(path); | |
CoTaskMemFree(path); | |
} | |
p_item->Release(); | |
} | |
} | |
p_items->Release(); | |
are_all_operation_success = true; | |
} | |
if (p_file_open) | |
p_file_open->Release(); | |
return are_all_operation_success; | |
} | |
/** | |
* @brief Open a dialog to save an item. | |
* @param path Specifies the reference to the string that will receive the target save path. [IN] | |
* @param defaultFileName Specifies the default save file name. (optional) | |
* @param pFilterInfo Specifies the pointer to the pair that contains filter information. (optional) | |
* @note If no path was selected, the function still returns true, and the given string is unmodified. | |
* @note `<windows.h>`, `<string>`, `<vector>`, `<shobjidl.h>` | |
* @return Returns true if all the operations are successfully performed, false otherwise. | |
*/ | |
bool SaveFileDialog(std::wstring &path, std::wstring defaultFileName = L"", std::pair<COMDLG_FILTERSPEC *, int> *pFilterInfo = nullptr) | |
{ | |
IFileSaveDialog *p_file_save = nullptr; | |
bool are_all_operation_success = false; | |
while (!are_all_operation_success) | |
{ | |
HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL, | |
IID_IFileSaveDialog, reinterpret_cast<void **>(&p_file_save)); | |
if (FAILED(hr)) | |
break; | |
if (!pFilterInfo) | |
{ | |
COMDLG_FILTERSPEC save_filter[1]; | |
save_filter[0].pszName = L"All files"; | |
save_filter[0].pszSpec = L"*.*"; | |
hr = p_file_save->SetFileTypes(1, save_filter); | |
if (FAILED(hr)) | |
break; | |
hr = p_file_save->SetFileTypeIndex(1); | |
if (FAILED(hr)) | |
break; | |
} | |
else | |
{ | |
hr = p_file_save->SetFileTypes(pFilterInfo->second, pFilterInfo->first); | |
if (FAILED(hr)) | |
break; | |
hr = p_file_save->SetFileTypeIndex(1); | |
if (FAILED(hr)) | |
break; | |
} | |
if (!defaultFileName.empty()) | |
{ | |
hr = p_file_save->SetFileName(defaultFileName.c_str()); | |
if (FAILED(hr)) | |
break; | |
} | |
hr = p_file_save->Show(NULL); | |
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) // No item was selected. | |
{ | |
are_all_operation_success = true; | |
break; | |
} | |
else if (FAILED(hr)) | |
break; | |
IShellItem *p_item; | |
hr = p_file_save->GetResult(&p_item); | |
if (FAILED(hr)) | |
break; | |
PWSTR item_path; | |
hr = p_item->GetDisplayName(SIGDN_FILESYSPATH, &item_path); | |
if (FAILED(hr)) | |
break; | |
path = item_path; | |
CoTaskMemFree(item_path); | |
p_item->Release(); | |
are_all_operation_success = true; | |
} | |
if (p_file_save) | |
p_file_save->Release(); | |
return are_all_operation_success; | |
} | |
int main() | |
{ | |
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); | |
if (FAILED(hr)) | |
{ | |
std::cout << "Failed to initialize COM library.\n"; | |
return -1; | |
} | |
// Select an example. | |
std::cout << "1. Select an item.\n"; | |
std::cout << "2. Select a folder.\n"; | |
std::cout << "3. Select multiple items.\n"; | |
std::cout << "4. Save an item.\n"; | |
std::cout << "5. Save an item with filters.\n"; | |
std::cout << "Select an example: "; | |
int choice = 0; | |
std::cin >> choice; | |
switch (choice) | |
{ | |
// Example: Select an item. | |
case 1: | |
{ | |
std::vector<std::wstring> paths; | |
if (OpenFileDialog(paths)) | |
{ | |
if (!paths.empty()) | |
{ | |
std::cout << "Total items: " << paths.size() << "\n"; | |
for (int i = 0; i < static_cast<int>(paths.size()); ++i) | |
std::wcout << L"Path " << std::to_wstring(i + 1) << L": " << paths[i] << L"\n"; | |
} | |
else | |
std::cout << "No item were selected.\n"; | |
} | |
break; | |
} | |
// Example: Select a folder. | |
case 2: | |
{ | |
std::vector<std::wstring> paths; | |
if (OpenFileDialog(paths, true)) | |
{ | |
if (!paths.empty()) | |
{ | |
std::cout << "Total items: " << paths.size() << "\n"; | |
for (int i = 0; i < static_cast<int>(paths.size()); ++i) | |
std::wcout << L"Path " << std::to_wstring(i + 1) << L": " << paths[i] << L"\n"; | |
} | |
else | |
std::cout << "No item were selected.\n"; | |
} | |
break; | |
} | |
// Example: Select multiple items. | |
case 3: | |
{ | |
std::vector<std::wstring> paths; | |
if (OpenFileDialog(paths, false, true)) | |
{ | |
if (!paths.empty()) | |
{ | |
std::cout << "Total items: " << paths.size() << "\n"; | |
for (int i = 0; i < static_cast<int>(paths.size()); ++i) | |
std::wcout << L"Path " << std::to_wstring(i + 1) << L": " << paths[i] << L"\n"; | |
} | |
else | |
std::cout << "No item were selected.\n"; | |
} | |
break; | |
} | |
// Example: Save an item. | |
case 4: | |
{ | |
std::wstring path = L""; | |
if (SaveFileDialog(path, L"Some file.txt")) | |
{ | |
if (!path.empty()) | |
{ | |
std::wcout << L"Selected save path: " << path << L"\n"; | |
} | |
else | |
std::cout << "No item were selected.\n"; | |
} | |
break; | |
} | |
// Example: Save an item with filters. | |
case 5: | |
{ | |
std::wstring path = L""; | |
const unsigned int total_filters = 3; | |
COMDLG_FILTERSPEC filters[total_filters]; | |
filters[0].pszName = L"All files. (*.*)"; | |
filters[0].pszSpec = L"*.*"; | |
filters[1].pszName = L"Image files. (.bmp, .jpg, .png)"; | |
filters[1].pszSpec = L"*.bmp;*.jpg;*.png"; | |
filters[2].pszName = L"Specific file. (unique_file.txt)"; | |
filters[2].pszSpec = L"unique_file.txt"; | |
std::pair<COMDLG_FILTERSPEC *, int> filter_info = std::make_pair<COMDLG_FILTERSPEC *, int>(filters, total_filters); | |
if (SaveFileDialog(path, L"", &filter_info)) | |
{ | |
if (!path.empty()) | |
{ | |
std::wcout << L"Selected save path: " << path << L"\n"; | |
} | |
else | |
std::cout << "No item were selected.\n"; | |
} | |
break; | |
} | |
} | |
CoUninitialize(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment