Created
May 20, 2025 15:34
-
-
Save Kreijstal/bb4d3633c622512ae3de84c2dd6a35ae to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import ctypes | |
from ctypes import wintypes | |
# Define constants from your C code if needed (e.g. MAX_PATH used in buffer creation) | |
MAX_PATH_FOR_BUFFER = 260 # Or a larger reasonable size | |
# Load the DLL | |
try: | |
# Ensure the DLL is in the same directory as the script, | |
# or in a directory on the system's PATH, or provide the full path. | |
picker_dll = ctypes.CDLL(r"./choose.dll") # Use './' for current directory | |
except OSError as e: | |
print(f"Error loading DLL: {e}") | |
print("Ensure 'folderpicker_c.dll' is compiled and in the correct location.") | |
exit() | |
try: | |
ShowModernFolderDialogC = picker_dll.ShowModernFolderDialogC | |
ShowModernFolderDialogC.argtypes = [ | |
wintypes.HWND, # hwndOwner | |
wintypes.LPCWSTR, # lpszTitle | |
wintypes.LPWSTR, # lpszSelectedPath (buffer) | |
ctypes.c_int, # nMaxPath | |
wintypes.LPCWSTR # lpszInitialPath | |
] | |
ShowModernFolderDialogC.restype = wintypes.BOOL # Returns TRUE on success, FALSE on cancel/error | |
except AttributeError as e: | |
print(f"Error accessing function in DLL: {e}") | |
print("Ensure 'ShowModernFolderDialogC' is correctly exported from the DLL.") | |
exit() | |
def select_folder_via_dll(owner_hwnd=0, title="Select a Folder", initial_path=None): | |
""" | |
Calls the C DLL to show the modern folder selection dialog. | |
Returns the selected path as a string, or None if cancelled or error. | |
""" | |
selected_path_buffer = ctypes.create_unicode_buffer(MAX_PATH_FOR_BUFFER) | |
# Convert Python strings to LPCWSTR for the DLL | |
ctypes_title = wintypes.LPCWSTR(title) if title else None | |
ctypes_initial_path = wintypes.LPCWSTR(initial_path) if initial_path else None | |
ctypes_owner_hwnd = wintypes.HWND(owner_hwnd) if owner_hwnd is not None else wintypes.HWND(0) | |
print(f"Calling DLL: Title='{title}', InitialPath='{initial_path}', OwnerHWND={owner_hwnd}") | |
success = ShowModernFolderDialogC( | |
ctypes_owner_hwnd, | |
ctypes_title, | |
selected_path_buffer, | |
MAX_PATH_FOR_BUFFER, | |
ctypes_initial_path | |
) | |
if success: | |
print("DLL call successful.") | |
return selected_path_buffer.value | |
else: | |
print("DLL call indicated cancellation or an error.") | |
return None | |
if __name__ == "__main__": | |
path1 = select_folder_via_dll(title="Select an Output Folder)") | |
if path1: | |
print(f"DLL - Test 1 Selected: {path1}") | |
else: | |
print("DLL - Test 1 Cancelled or failed.") | |
This file contains hidden or 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
/*gcc -shared -o choose.dll choose.c -lole32 -lshell32 -luuid -DUNICODE -D_UNICODE*/ | |
// folderpicker_c_dll.c | |
#define CINTERFACE | |
#define COBJMACROS | |
#include <windows.h> | |
#include <shobjidl.h> | |
#include <objbase.h> | |
#include <wchar.h> // For wcslen, wcsncpy_s if strsafe.h is problematic | |
#include <strsafe.h> // <--- ADDED THIS FOR StringCch... FUNCTIONS | |
#if defined(__GNUC__) | |
#define DLL_EXPORT __attribute__((dllexport)) | |
#else | |
#define DLL_EXPORT __declspec(dllexport) | |
#endif | |
DLL_EXPORT BOOL ShowModernFolderDialogC( | |
HWND hwndOwner, | |
const WCHAR *lpszTitle, | |
WCHAR *lpszSelectedPath, | |
int nMaxPath, | |
const WCHAR *lpszInitialPath | |
) { | |
BOOL bSuccess = FALSE; | |
HRESULT hr; | |
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); | |
if (FAILED(hr) && hr == RPC_E_CHANGED_MODE) { | |
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | |
} | |
if (SUCCEEDED(hr)) { | |
IFileOpenDialog *pFileOpenDialog = NULL; | |
hr = CoCreateInstance( | |
&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, | |
&IID_IFileOpenDialog, (void**)&pFileOpenDialog | |
); | |
if (SUCCEEDED(hr)) { | |
DWORD dwOptions; | |
hr = IFileOpenDialog_GetOptions(pFileOpenDialog, &dwOptions); | |
if (SUCCEEDED(hr)) { | |
hr = IFileOpenDialog_SetOptions(pFileOpenDialog, dwOptions | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM); | |
} | |
if (SUCCEEDED(hr) && lpszTitle != NULL && lpszTitle[0] != L'\0') { | |
hr = IFileOpenDialog_SetTitle(pFileOpenDialog, lpszTitle); | |
} | |
if (SUCCEEDED(hr) && lpszInitialPath != NULL && lpszInitialPath[0] != L'\0') { | |
IShellItem *pInitialItem = NULL; | |
hr = SHCreateItemFromParsingName(lpszInitialPath, NULL, &IID_IShellItem, (void**)&pInitialItem); | |
if (SUCCEEDED(hr) && pInitialItem) { | |
hr = IFileOpenDialog_SetFolder(pFileOpenDialog, pInitialItem); | |
IShellItem_Release(pInitialItem); | |
} else { | |
hr = S_OK; | |
} | |
} | |
if (SUCCEEDED(hr)) { | |
hr = IFileOpenDialog_Show(pFileOpenDialog, hwndOwner); | |
} | |
if (SUCCEEDED(hr)) { | |
IShellItem *pSelectedItem = NULL; | |
hr = IFileOpenDialog_GetResult(pFileOpenDialog, &pSelectedItem); | |
if (SUCCEEDED(hr) && pSelectedItem) { | |
PWSTR pszFilePath = NULL; | |
hr = IShellItem_GetDisplayName(pSelectedItem, SIGDN_FILESYSPATH, &pszFilePath); | |
if (SUCCEEDED(hr) && pszFilePath) { | |
// Use String Safe functions | |
size_t cchFilePath = 0; // Use size_t for character counts | |
// Get the length of the source string | |
hr = StringCchLengthW(pszFilePath, STRSAFE_MAX_CCH, &cchFilePath); | |
if (SUCCEEDED(hr)) { | |
// Check if it fits (including null terminator) | |
if ((cchFilePath + 1) <= (size_t)nMaxPath) { | |
hr = StringCchCopyW(lpszSelectedPath, nMaxPath, pszFilePath); | |
if (SUCCEEDED(hr)) { | |
bSuccess = TRUE; | |
} | |
} else { | |
// Path too long for buffer, could set an error or truncate | |
// For now, just fail bSuccess. | |
// Or, you could copy nMaxPath-1 chars and null terminate. | |
lpszSelectedPath[0] = L'\0'; // Ensure buffer is empty on failure to copy | |
} | |
} | |
CoTaskMemFree(pszFilePath); | |
} | |
IShellItem_Release(pSelectedItem); | |
} | |
} | |
IFileOpenDialog_Release(pFileOpenDialog); | |
} | |
CoUninitialize(); | |
} | |
return bSuccess; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment