Skip to content

Commit

Permalink
Updated code for refresh_file_explorer.exe. Now getting paths from al…
Browse files Browse the repository at this point in the history
…l File Explorer windows. #115.
  • Loading branch information
end2endzone committed Jan 7, 2024
1 parent f8f632a commit 091e43d
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 63 deletions.
4 changes: 4 additions & 0 deletions src/refresh_file_explorer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ add_executable(refresh_file_explorer WIN32
${SHELLANYTHING_EXPORT_HEADER}
${SHELLANYTHING_VERSION_HEADER}
${SHELLANYTHING_CONFIG_HEADER}
file_explorer.cpp
file_explorer.h
main.cpp
user.cpp
user.h
)

# Force UNICODE for target
Expand Down
208 changes: 208 additions & 0 deletions src/refresh_file_explorer/file_explorer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// https://learn.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-ccomptr-and-ccomqiptr-instances?view=msvc-170
// https://forums.codeguru.com/showthread.php?345012-Access-violation-using-CComPtr-lt-gt

#include "file_explorer.h"
#include "user.h"

#include <objbase.h>
#include <comdef.h>
#include <atlbase.h>
#include <shlobj.h>
#include <exdisp.h>

#include "rapidassist/undef_windows_macros.h"
#include "rapidassist/unicode.h"
#include "rapidassist/errors.h"
#include "rapidassist/filesystem_utf8.h"
#include "rapidassist/environment_utf8.h"

bool GetFileExplorerWindowPaths(Utf8FileList& files)
{
files.clear();

CoInitialize(NULL);

HRESULT hr;
wchar_t szPath[50 * MAX_PATH];
DWORD size_in_characters = sizeof(szPath) / 2;

CComPtr<IShellWindows> pShellWindows;
hr = pShellWindows.CoCreateInstance(
CLSID_ShellWindows,
NULL,
CLSCTX_ALL
);
if (FAILED(hr))
{
CoUninitialize();
return false;
}

// Browse shell windows one by one
bool has_more = true;
VARIANT index;
V_VT(&index) = VT_I4;
V_I4(&index) = 0;
while (has_more)
{
// Check for a shell window at index
IDispatch* pDisp;
hr = pShellWindows->Item(index, &pDisp);
if (hr == S_FALSE)
{
has_more = false;
}
else if (hr == S_OK) // Warning, do not check 'hr' with SUCCEEDED() macro since S_FALSE is considered a success.
{
// Handle IDispatch* graceful destruction
CComPtr<IDispatch> pItem = pDisp;

// Get IWebBrowserApp from Item
CComPtr<IWebBrowserApp> pWebApp;
hr = pItem->QueryInterface(IID_PPV_ARGS(&pWebApp));
if (SUCCEEDED(hr))
{
// Get IServiceProvider from IWebBrowserApp
CComPtr<IServiceProvider> pServiceProvider;
hr = pWebApp->QueryInterface(IID_PPV_ARGS(&pServiceProvider));
if (SUCCEEDED(hr))
{
// Get IShellBrowser from IServiceProvider
CComPtr<IShellBrowser> pShellBrowser;
hr = pServiceProvider->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&pShellBrowser));
if (SUCCEEDED(hr))
{
IShellView* psv;
hr = pShellBrowser->QueryActiveShellView(&psv);
if (SUCCEEDED(hr))
{
// Handle IShellView* graceful destruction
CComPtr<IShellView> pShellView = psv;

// Get IFolderView from IShellView
CComPtr<IFolderView> pFolderView;
hr = pShellView->QueryInterface(IID_PPV_ARGS(&pFolderView));
if (SUCCEEDED(hr))
{
IPersistFolder2* ppf2;
hr = pFolderView->GetFolder(IID_IPersistFolder2, (void**)&ppf2);
if (SUCCEEDED(hr))
{
// Handle IPersistFolder2* graceful destruction
CComPtr<IPersistFolder2> pPersistFolder2 = ppf2;

LPITEMIDLIST pidlFolder;
if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder)))
{
szPath[0] = '\0';
if (SHGetPathFromIDListEx(pidlFolder, szPath, size_in_characters, GPFIDL_DEFAULT))
{
// Convert from WIDE to UTF8
std::string str_utf8 = ra::unicode::UnicodeToUtf8(szPath);
files.push_back(str_utf8);
}
else
{
//not a directory
}
CoTaskMemFree(pidlFolder);
}
}
}
}
}
}
}
}

// Next Item
V_I4(&index)++;
}

CoUninitialize();

return true;
}

bool OpenFileExplorerWindow(const std::string& path)
{
// Find the absolute path of explorer.exe
std::string explorer_path_utf8 = ra::filesystem::FindFileFromPathsUtf8("explorer.exe");
if (explorer_path_utf8.empty())
return false;
std::wstring explorer_path_wide = ra::unicode::Utf8ToUnicode(explorer_path_utf8);
const wchar_t * lpApplicationName = explorer_path_wide.c_str();

// Convert the given path to wide string
std::wstring path_wide = ra::unicode::Utf8ToUnicode(path);

// Build a command line (which is including the path to the executable)
std::wstring command_line;
command_line += explorer_path_wide;

// Add the given path as an argument
// Wrap the path in double quotes to handle spaces.
// And make a copy of the string for stupid WIN32 api...
command_line += L" ";
command_line += L"\"";
command_line += path_wide;
command_line += L"\"";
wchar_t * lpCommandLine = new wchar_t[command_line.size() + 1];
StrCpyW(lpCommandLine, command_line.c_str());

//launch a new process with the command line
PROCESS_INFORMATION pi = { 0 };
STARTUPINFOW si = { 0 };
si.cb = sizeof(STARTUPINFO);
BOOL created = CreateProcessW(
lpApplicationName, // Using module name
lpCommandLine, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
if (created != 0)
{
//Wait for the application to initialize properly
WaitForInputIdle(pi.hProcess, INFINITE);

//Extract the program id
DWORD process_id = pi.dwProcessId;

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

delete[] lpCommandLine;
return true;
}

delete[] lpCommandLine;
return false;
}

void TestOpenFolderUnicode()
{
// Test:
const std::string test_path = "D:\\" "\xCE" "\xA8"; // psi
//const std::string test_path = "C:\\temp";
bool opened = OpenFileExplorerWindow(test_path);
if (!opened)
{
ra::errors::errorcode_t error_code = ra::errors::GetLastErrorCode();
std::string error_desc = ra::errors::GetErrorCodeDescription(error_code);

std::wstring message;
message += L"Failed to open directory:\n";
message += ra::unicode::Utf8ToUnicode(test_path);
message += L"\n";
message += L"\n";
message += ra::unicode::Utf8ToUnicode(error_desc);

ShowErrorMessage(message);
}
}
26 changes: 26 additions & 0 deletions src/refresh_file_explorer/file_explorer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <string>
#include <vector>

typedef std::vector<std::string> Utf8FileList;

/// <summary>
/// Get the list of paths for all opened File Explorer windows.
/// The paths are returned in utf8 format.
/// </summary>
/// <param name="files">The output file path list in utf8 format.</param>
/// <returns>Returns true when the function has succeeded. Returns false otherwise.</returns>
bool GetFileExplorerWindowPaths(Utf8FileList& files);

/// <summary>
/// Open File Explorer to the given location.
/// </summary>
/// <param name="path">The path location in utf8 format.</param>
/// <returns>Returns true when the function has succeeded. Returns false otherwise.</returns>
bool OpenFileExplorerWindow(const std::string& path);

/// <summary>
/// Test to open directory unicode directory `D:\Ψ`.
/// </summary>
void TestOpenFolderUnicode();
87 changes: 24 additions & 63 deletions src/refresh_file_explorer/main.cpp
Original file line number Diff line number Diff line change
@@ -1,74 +1,35 @@
#include <windows.h>
#include "file_explorer.h"
#include "user.h"

const TCHAR g_szClassName[] = L"myWindowClass";

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
#include "rapidassist/process_utf8.h"
#include "rapidassist/filesystem_utf8.h"
#include "rapidassist/unicode.h"
#include "rapidassist/errors.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;

//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if (!RegisterClassEx(&wc))
// Get prompt confirmation
bool confirmed = GetUserConfirmation();
if (!confirmed)
return 1;

TestOpenFolderUnicode();
return 9;

// Get all the paths from File Explorer windows
Utf8FileList files;
bool success = GetFileExplorerWindowPaths(files);
if (!success)
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
std::wstring message;
message += L"ERROR!\n";
message += L"Failed getting paths from File Explorer windows.";
ShowErrorMessage(message);
return 2;
}

// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
L"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL);

if (hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Step 3: The Message Loop
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
return 0;
}
Loading

0 comments on commit 091e43d

Please sign in to comment.