Skip to content

Instantly share code, notes, and snippets.

@Kreijstal
Created May 20, 2025 15:34
Show Gist options
  • Save Kreijstal/bb4d3633c622512ae3de84c2dd6a35ae to your computer and use it in GitHub Desktop.
Save Kreijstal/bb4d3633c622512ae3de84c2dd6a35ae to your computer and use it in GitHub Desktop.
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.")
/*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