For special folders, you should get the GUID back. I'll leave interpreting it up to you back in the script; the injected function is quite large already.
Thanks for the code - I had a similar idea in mind, but no idea how to do it in code.
as for getting the path/selected items from common item dialogs, try this revised version of the script:
Code: Select all
#NoEnv
;#NoTrayIcon
ListLines, Off
SetBatchLines, -1
#KeyHistory 0
cleanup := True ; unsurprisingly, you will almost always want this on
main(WinExist("Select File(s) ahk_exe AutoHotkey.exe")), cleanup ? ExitApp : return
main(fileDialogHwnd)
{
try {
if (!fileDialogHwnd)
throw
if (!(fileDialogTid := DllCall("GetWindowThreadProcessId", "Ptr", fileDialogHwnd, "UInt*", fileDialogPid, "UInt")) || !fileDialogPid)
throw
MsgWaitForMultipleObjectsEx := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandleW", "WStr", "user32.dll", "Ptr"), "AStr", "MsgWaitForMultipleObjectsEx", "Ptr")
; open process for writing into
if (!(hProcess := DllCall("OpenProcess", "UInt", PROCESS_CREATE_THREAD := 0x0002 | PROCESS_QUERY_INFORMATION := 0x0400 | PROCESS_VM_OPERATION := 0x0008 | PROCESS_VM_READ := 0x0010 | PROCESS_VM_WRITE := 0x0020, "Int", False, "UInt", fileDialogPid, "Ptr")))
throw
; get addresses of all modules loaded in remote process
MAX_PATH := 260
INFINITE := 0xffffffff
LIST_MODULES_DEFAULT := 0x00
Loop {
if (!DllCall("psapi\EnumProcessModulesEx", "Ptr", hProcess, "Ptr", 0, "UInt", 0, "UInt*", cbNeeded, "UInt", LIST_MODULES_DEFAULT))
throw
VarSetCapacity(hModules, cbNeeded, 0)
} until (DllCall("psapi\EnumProcessModulesEx", "Ptr", hProcess, "Ptr", &hModules, "UInt", cbNeeded, "UInt*", cbNeeded, "UInt", LIST_MODULES_DEFAULT))
VarSetCapacity(modName, (MAX_PATH + 2) * 2)
Loop % cbNeeded / A_PtrSize {
if (DllCall("psapi\GetModuleBaseName", "Ptr", hProcess, "Ptr", NumGet(hModules, A_PtrSize * (A_Index - 1), "Ptr"), "Str", modName, "UInt", MAX_PATH)) {
if (!user32 && modName = "user32.dll") {
user32 := NumGet(hModules, A_PtrSize * (A_Index - 1), "Ptr") ; store address of user32.dll present in remote process
} else if (!kernel32 && modName = "kernel32.dll") {
kernel32 := NumGet(hModules, A_PtrSize * (A_Index - 1), "Ptr")
} else if (!shlwapi && modName = "shlwapi.dll") {
shlwapi := NumGet(hModules, A_PtrSize * (A_Index - 1), "Ptr")
} else if (!ntdll && modName = "ntdll.dll") {
ntdll := NumGet(hModules, A_PtrSize * (A_Index - 1), "Ptr")
} else if (!ole32 && modName = "ole32.dll") {
ole32 := NumGet(hModules, A_PtrSize * (A_Index - 1), "Ptr")
}
}
}
if (!user32 || !kernel32 || !shlwapi || !ntdll || !ole32)
throw
; prepare ThreadData struct
VarSetCapacity(threadData, A_PtrSize + (16 * 4) + 4 + (A_PtrSize * 4), 0) ; keep in alignment with ThreadData struct definition inside the C code
; allocate space for a *separate* copy of ThreadData inside the process - we need to get its address before the code is compiled
if (!(pRemoteThreadData := DllCall("VirtualAllocEx", "Ptr", hProcess, "Ptr", 0, "Ptr", VarSetCapacity(threadData), "UInt", MEM_COMMIT := 0x00001000, "UInt", PAGE_READWRITE := 0x04, "Ptr")))
throw
; Since the injected code doesn't have the benefit of a linker to tell it where library functions can be found, we do the resolving for it here
wcslen := ProcAddressFromRemoteProcess(hProcess, ntdll, "wcslen")
CloseHandle := ProcAddressFromRemoteProcess(hProcess, kernel32, "CloseHandle")
WaitForSingleObject := ProcAddressFromRemoteProcess(hProcess, kernel32, "WaitForSingleObject")
SetEvent := ProcAddressFromRemoteProcess(hProcess, kernel32, "SetEvent")
VirtualAllocEx := ProcAddressFromRemoteProcess(hProcess, kernel32, "VirtualAllocEx")
CreateEventW := ProcAddressFromRemoteProcess(hProcess, kernel32, "CreateEventW")
SetWindowsHookExW := ProcAddressFromRemoteProcess(hProcess, user32, "SetWindowsHookExW")
PostMessageW := ProcAddressFromRemoteProcess(hProcess, user32, "PostMessageW")
SendMessageW := ProcAddressFromRemoteProcess(hProcess, user32, "SendMessageW")
CallNextHookEx := ProcAddressFromRemoteProcess(hProcess, user32, "CallNextHookEx")
UnhookWindowsHookEx := ProcAddressFromRemoteProcess(hProcess, user32, "UnhookWindowsHookEx")
MessageBoxW := ProcAddressFromRemoteProcess(hProcess, user32, "MessageBoxW")
wsprintfW := ProcAddressFromRemoteProcess(hProcess, user32, "wsprintfW")
StrRetToBufW := ProcAddressFromRemoteProcess(hProcess, shlwapi, "StrRetToBufW")
CoTaskMemFree := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandleW", "WStr", "ole32.dll", "Ptr"), "AStr", "CoTaskMemFree", "Ptr") ; ProcAddressFromRemoteProcess bug - it cannot get these functions properly...
injected_func =
(
/* Do the normal thing, instead. */
#define NULL ((void *)0)
#define __declspec(x) __attribute__((x))
#define __stdcall __attribute__((__stdcall__))
#define __cdecl __attribute__((__cdecl__))
typedef unsigned short wchar_t;
#define DECLSPEC_IMPORT __declspec(dllimport)
#define WINUSERAPI DECLSPEC_IMPORT
#define WINAPI __stdcall
#define STDMETHODCALLTYPE WINAPI
typedef wchar_t WCHAR;
#define CONST const
typedef CONST WCHAR *LPCWSTR,*PCWSTR;
typedef WCHAR *NWPSTR,*LPWSTR,*PWSTR;
typedef unsigned int UINT, DWORD;
typedef unsigned char BYTE;
typedef BYTE* PBYTE;
typedef void* LPVOID;
typedef void *HANDLE;
#define DECLARE_HANDLE(name) struct name##__ { int q12; }; typedef struct name##__ *name
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HHOOK);
DECLARE_HANDLE(HINSTANCE);
#ifdef _WIN64
#define __int64 long long
typedef __int64 LONG_PTR,*PLONG_PTR;
typedef unsigned __int64 UINT_PTR,*PUINT_PTR;
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
#else
typedef long LONG_PTR,*PLONG_PTR;
typedef unsigned int UINT_PTR,*PUINT_PTR;
typedef unsigned long ULONG_PTR, *PULONG_PTR;
#endif
typedef long HRESULT;
typedef unsigned long ULONG;
typedef UINT_PTR WPARAM;
typedef LONG_PTR LPARAM;
typedef LONG_PTR LRESULT;
typedef ULONG_PTR SIZE_T, *PSIZE_T;
typedef struct tagPOINT {
long x;
long y;
} POINT,*PPOINT,*NPPOINT,*LPPOINT;
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG,*PMSG,*NPMSG,*LPMSG;
#define WM_NULL 0x0000
typedef unsigned short USHORT;
#pragma pack(push, 1)
typedef struct _SHITEMID
{
USHORT cb;
BYTE abID[ 1 ];
} SHITEMID;
typedef struct _ITEMIDLIST
{
SHITEMID mkid;
} ITEMIDLIST;
#pragma pack(pop)
typedef ITEMIDLIST *LPITEMIDLIST;
#pragma pack(push, 8)
typedef struct _STRRET
{
UINT uType;
/* [switch_is][switch_type] */ union
{
/* [case()][string] */ LPWSTR pOleStr;
/* [case()] */ UINT uOffset;
/* [case()] */ char cStr[ 260 ];
} DUMMYUNIONNAME;
} STRRET;
#pragma pack(pop)
typedef STRRET *LPSTRRET;
typedef struct {
HRESULT (STDMETHODCALLTYPE *QueryInterface)(void * This, void* riid, void **ppvObject);
ULONG (STDMETHODCALLTYPE *AddRef)(void * This);
ULONG (STDMETHODCALLTYPE *Release)(void * This);
} IUnknownVtbl;
typedef struct {
IUnknownVtbl unkVtbl;
LPVOID dummyq[12];
HRESULT (STDMETHODCALLTYPE *QueryActiveShellView)(void * This, void **ppshv);
} partIShellBrowserVtbl;
typedef struct {
IUnknownVtbl unkVtbl;
LPVOID dummy[2];
HRESULT ( STDMETHODCALLTYPE *GetFolder )( void * This, void* riid, void **ppv);
LPVOID dummy3;
HRESULT (STDMETHODCALLTYPE *ItemCount)(void * This, UINT uFlags, int* pcItems);
HRESULT (STDMETHODCALLTYPE *Items)(void * This, UINT uFlags, void* riid, void **ppshv);
} partIFolderViewVtbl;
typedef struct {
IUnknownVtbl unkVtbl;
LPVOID dummy[8];
HRESULT ( STDMETHODCALLTYPE *GetDisplayNameOf )( void * This, LPITEMIDLIST pidl, UINT uFlags, STRRET *pName);
} partIShellFolderVtbl;
typedef struct {
IUnknownVtbl unkVtbl;
HRESULT (STDMETHODCALLTYPE *Next)(void * This, ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
} partIEnumIDListVtbl;
typedef struct {
IUnknownVtbl unkVtbl;
LPVOID dummy[2];
HRESULT (STDMETHODCALLTYPE *GetCurFolder)(void * This, LPITEMIDLIST *ppidl);
} partIPersistFolder2Vtbl;
typedef struct {
IUnknownVtbl *lpVtbl;
} IUnknown;
// Badly defined function type definitions
typedef LPVOID (WINAPI *VIRTUALALLOCEX)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
typedef SIZE_T (__cdecl *WCSLEN)(LPWSTR);
typedef HHOOK (WINAPI *SETWINDOWSHOOKEXW)(int, LPVOID, HINSTANCE, DWORD);
typedef LRESULT (WINAPI *CALLNEXTHOOKEX)(HHOOK, int, WPARAM, LPARAM);
typedef int (WINAPI *UNHOOKWINDOWSHOOKEX)(HHOOK);
typedef LRESULT (WINAPI *SENDMESSAGEW)(HWND, UINT, WPARAM, LPARAM);
typedef int (WINAPI *POSTMESSAGEW)(HWND, UINT, WPARAM, LPARAM);
typedef HANDLE (WINAPI *CREATEEVENTW)(LPVOID, int, int, LPVOID);
typedef int (WINAPI *SETEVENT)(HANDLE);
typedef DWORD (WINAPI *WAITFORSINGLEOBJECT)(HANDLE,DWORD);
typedef int (WINAPI *CLOSEHANDLE)(HANDLE);
typedef void (WINAPI *COTASKMEMFREE)(LPVOID);
typedef int (WINAPI *MESSAGEBOXW)(HWND, LPCWSTR, LPCWSTR, UINT);
typedef int (__cdecl *WSPRINTFW)(LPWSTR, LPWSTR, ...);
typedef HRESULT (WINAPI *STRRETTOBUFW)(STRRET*, LPITEMIDLIST, LPWSTR, UINT);
/* --------------------------------------------------------------------------------------
-----------------------------------Real code follows----------------------------------
-------------------------------------------------------------------------------------- */
#pragma pack(push, 1) // disable struct padding to make things a bit easier for me when populating the struct in AutoHotkey below
typedef struct {
LPVOID lpHookFunc;
char IID_IFolderView[16]; // CBA to include the proper definition for GUID
char IID_IEnumIDList[16];
char IID_IPersistFolder2[16];
char IID_IShellFolder[16];
int openFolderPathInstead;
SIZE_T allocatedSize;
LPWSTR selectedFilenames;
HHOOK msgHook;
HANDLE hThreadWaitEvent;
} ThreadData, *PThreadData;
#pragma pack(pop)
LRESULT WINAPI GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
CALLNEXTHOOKEX CallNextHookEx = (CALLNEXTHOOKEX)%CallNextHookEx%;
if (code == 0) { // HC_ACTION
PMSG msg = (PMSG)lParam;
if (msg->hwnd == (HWND)%fileDialogHwnd% && msg->message == WM_NULL) { // only work if activated through the message we posted
PThreadData threadData = (PThreadData)%pRemoteThreadData%;
// We're on the main thread, no need for the hook to be called again after this
UNHOOKWINDOWSHOOKEX UnhookWindowsHookEx = (UNHOOKWINDOWSHOOKEX)%UnhookWindowsHookEx%;
UnhookWindowsHookEx(threadData->msgHook);
threadData->msgHook = 0;
SENDMESSAGEW SendMessageW = (SENDMESSAGEW)%SendMessageW%;
IUnknown *iShellBrowser = (IUnknown*)SendMessageW((HWND)%fileDialogHwnd%, 0x407, 0, 0); // WM_GETISHELLBROWSER
if (iShellBrowser) {
iShellBrowser->lpVtbl->AddRef(iShellBrowser);
IUnknown *iShellView = NULL;
((partIShellBrowserVtbl*)iShellBrowser->lpVtbl)->QueryActiveShellView(iShellBrowser, (LPVOID*)&iShellView);
if (iShellView) {
// read https://blogs.msdn.microsoft.com/oldnewthing/20040720-00/?p=38393 to understand how this works ('cause I sure as hell don't know)
IUnknown *iFolderView = NULL;
iShellView->lpVtbl->QueryInterface(iShellView, &(threadData->IID_IFolderView), (LPVOID*)&iFolderView);
if (iFolderView) {
IUnknown *iPersistFolder = NULL;
((partIFolderViewVtbl*)iFolderView->lpVtbl)->GetFolder(iFolderView, threadData->IID_IPersistFolder2, (LPVOID*)&iPersistFolder);
if (iPersistFolder) {
IUnknown *iShellFolder = NULL;
iPersistFolder->lpVtbl->QueryInterface(iPersistFolder, threadData->IID_IShellFolder, (LPVOID*)&iShellFolder);
if (iShellFolder) {
int cItems = 0;
VIRTUALALLOCEX VirtualAllocEx = (VIRTUALALLOCEX)%VirtualAllocEx%;
if (!threadData->openFolderPathInstead)
((partIFolderViewVtbl*)iFolderView->lpVtbl)->ItemCount(iFolderView, 0x1, &cItems); // SVGIO_SELECTION - find out how many selected items there are
cItems = ((threadData->openFolderPathInstead = cItems < 1)) ? 1 : cItems;
threadData->allocatedSize = (((%MAX_PATH% + 1) * sizeof(WCHAR)) * cItems) + (2 * sizeof(WCHAR)); // set aside enough space to hold the filenames, with null terminators of course, and extra space for the double-null terminator at the end
if ((threadData->selectedFilenames = VirtualAllocEx((HANDLE)-1, NULL, threadData->allocatedSize, %MEM_COMMIT%, %PAGE_READWRITE%))) { // use VirtualAllocEx because we can then free the memory from the AutoHotkey script
//for (;;) {
LPWSTR target = threadData->selectedFilenames;
WCSLEN wcslen = (WCSLEN)%wcslen%;
COTASKMEMFREE CoTaskMemFree = (COTASKMEMFREE)%CoTaskMemFree%;
STRRETTOBUFW StrRetToBufW = (STRRETTOBUFW)%StrRetToBufW%;
IUnknown *iEnumIdList = NULL;
LPITEMIDLIST pidlItem = NULL;
STRRET str;
int written = 0;
if (threadData->openFolderPathInstead) {
/* MESSAGEBOXW MessageBoxW = (MESSAGEBOXW)%MessageBoxW%;
WSPRINTFW wsprintfW = (WSPRINTFW)%wsprintfW%;
WCHAR fmt[] = L"`%ld";
WCHAR err[100];
wsprintfW(err, fmt, ((partIShellFolderVtbl*)iShellFolder->lpVtbl)->GetDisplayNameOf(iShellFolder, 0, 0x80058000, &str));
MessageBoxW(NULL, err, err, 0); */
if (((partIPersistFolder2Vtbl*)iPersistFolder->lpVtbl)->GetCurFolder(iPersistFolder, &pidlItem) >= 0)
goto writeString;
} else {
((partIFolderViewVtbl*)iFolderView->lpVtbl)->Items(iFolderView, 0x1, threadData->IID_IEnumIDList, (LPVOID*)&iEnumIdList);
if (iEnumIdList) {
ULONG celtFetched = 0;
while ((((partIEnumIDListVtbl*)iEnumIdList->lpVtbl)->Next(iEnumIdList, 1, &pidlItem, &celtFetched) >= 0) && celtFetched == 1 && written < cItems) { // while we keep getting a PIDL:
writeString:
if ((((partIShellFolderVtbl*)iShellFolder->lpVtbl)->GetDisplayNameOf(iShellFolder, threadData->openFolderPathInstead ? 0 : pidlItem, 0x80058000, &str) >= 0)) { // SIGDN_FILESYSPATH
if (StrRetToBufW(&str, pidlItem, target, %MAX_PATH%) >= 0) {
target += wcslen(target) + 1; // shift the pointer to now point just after the written string + the null terminator
written++; // make sure not to include any new paths that are included between us getting the count and actually iterating, just in case - CBA to realloc
}
}
CoTaskMemFree(pidlItem);
if (threadData->openFolderPathInstead)
break;
}
if (iEnumIdList)
iEnumIdList->lpVtbl->Release(iEnumIdList);
}
}
if (*target) {
*target = '\0'; // make sure string is double null-terminated
*target-- = '\0'; // make sure string has its normal null terminator
}
//break;
//}
} else {
threadData->allocatedSize = 0;
}
iShellFolder->lpVtbl->Release(iShellFolder);
}
iPersistFolder->lpVtbl->Release(iPersistFolder);
}
iFolderView->lpVtbl->Release(iFolderView);
}
iShellView->lpVtbl->Release(iShellView);
}
iShellBrowser->lpVtbl->Release(iShellBrowser);
}
SETEVENT SetEvent = (SETEVENT)%SetEvent%;
SetEvent(threadData->hThreadWaitEvent); // signal the event to get the ThreadFunc to exit
return 0;
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
}
UINT GetMsgProcSize(void) { return (PBYTE) GetMsgProcSize - (PBYTE) GetMsgProc; }
DWORD WINAPI ThreadProc(PThreadData threadData)
{
DWORD ret = 0;
if (threadData) {
CREATEEVENTW CreateEventW = (CREATEEVENTW)%CreateEventW%;
if ((threadData->hThreadWaitEvent = CreateEventW(NULL, 0, 0, NULL))) {
CLOSEHANDLE CloseHandle = (CLOSEHANDLE)%CloseHandle%;
SETWINDOWSHOOKEXW SetWindowsHookExW = (SETWINDOWSHOOKEXW)%SetWindowsHookExW%; // set up a hook so that we can run code on the same thread the open folder dialog box is displaying itself on - COM is usually touchy about multithread use
if ((threadData->msgHook = SetWindowsHookExW(3, threadData->lpHookFunc, 0, %fileDialogTid%))) { // WH_GETMESSAGE
POSTMESSAGEW PostMessageW = (POSTMESSAGEW)%PostMessageW%;
PostMessageW((HWND)%fileDialogHwnd%, WM_NULL, 0, 0); // fire up the hook function ASAP
WAITFORSINGLEOBJECT WaitForSingleObject = (WAITFORSINGLEOBJECT)%WaitForSingleObject%;
if (WaitForSingleObject(threadData->hThreadWaitEvent, %INFINITE%) == 0x00000000L) // WAIT_OBJECT_0
ret = 1;
}
CloseHandle(threadData->hThreadWaitEvent);
}
}
return ret;
}
UINT noCompetitionMandemFaster(void) { return (PBYTE) noCompetitionMandemFaster - (PBYTE) ThreadProc; }
)
if (!(ctx := CompileC(injected_func,, False)))
throw
if (!(size := DllCall("libtcc\tcc_relocate", "Ptr", ctx.tccState, "Ptr", 0, "CDecl"))) ; get size of compiled code
throw
if (!(pCompiled := DllCall("GlobalAlloc", "UInt", GMEM_FIXED := 0x0000, "Ptr", size, "Ptr"))) ; allocate memory for said code
throw
if (DllCall("libtcc\tcc_relocate", "Ptr", ctx.tccState, "Ptr", pCompiled, "CDecl") == -1) ; get tcc to write the compiled code into the allocated memory
throw
if (!DllCall("VirtualProtect", "Ptr", pCompiled, "Ptr", size, "UInt", PAGE_EXECUTE_READWRITE := 0x40, "UInt*", 0)) ; Make sure we can execute from memory chunk
throw
if (!(cbThreadRoutine := DllCall(DllCall("libtcc\tcc_get_symbol", "Ptr", ctx.tccState, "AStr", "noCompetitionMandemFaster", "CDecl Ptr"), "CDecl UInt"))) ; get the size the function takes up
throw
if (!(pThreadRoutine := DllCall("libtcc\tcc_get_symbol", "Ptr", ctx.tccState, "AStr", "_ThreadProc@" . (1 * A_PtrSize), "CDecl Ptr"))) ; fuck me... get pointer to thread function copied into remote process
throw
if (!(cbGetMsgProc := DllCall(DllCall("libtcc\tcc_get_symbol", "Ptr", ctx.tccState, "AStr", "GetMsgProcSize", "CDecl Ptr"), "CDecl UInt")))
throw
if (!(pGetMsgProc := DllCall("libtcc\tcc_get_symbol", "Ptr", ctx.tccState, "AStr", "_GetMsgProc@" . (3 * A_PtrSize), "CDecl Ptr")))
throw
; allocate memory for the process' copy of the message hook function
if (!(pGetMsgFunc := DllCall("VirtualAllocEx", "Ptr", hProcess, "Ptr", 0, "Ptr", cbGetMsgProc, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")))
throw
if (!DllCall("WriteProcessMemory", "Ptr", hProcess, "Ptr", pGetMsgFunc, "Ptr", pGetMsgProc, "Ptr", cbGetMsgProc, "Ptr", 0)) ; write the message hook function into the process' memory
throw
NumPut(pGetMsgFunc, threadData,, "Ptr") ; threadData->lpHookFunc - memory for this is allocated after this is compiled as I need to know the function's size, which I only get after compiling. So put the pointer to the function into the ThreadData struct
DllCall("ole32\CLSIDFromString", "WStr", "{cde725b0-ccc9-4519-917e-325d72fab4ce}", "Ptr", &threadData+A_PtrSize) ; threadData->IID_IFolderView - it's easier to get these from AutoHotkey beforehand
DllCall("ole32\CLSIDFromString", "WStr", "{000214F2-0000-0000-C000-000000000046}", "Ptr", &threadData+A_PtrSize+16) ; threadData->IID_IEnumIDList
DllCall("ole32\CLSIDFromString", "WStr", "{1AC3D9F0-175C-11d1-95BE-00609797EA4F}", "Ptr", &threadData+A_PtrSize+(16 * 2)) ; threadData->IID_IPersistFolder2
DllCall("ole32\CLSIDFromString", "WStr", "{000214E6-0000-0000-C000-000000000046}", "Ptr", &threadData+A_PtrSize+(16 * 3)) ; threadData->IID_IShellFolder
;NumPut(True, threadData, A_PtrSize+(16*4), "Int") ; threadData.openFolderPathInstead - uncomment if you want to get the path of the opened folder regardless of whether files are selected
if (!DllCall("WriteProcessMemory", "Ptr", hProcess, "Ptr", pRemoteThreadData, "Ptr", &threadData, "Ptr", VarSetCapacity(threadData), "Ptr", 0)) ; now copy the ThreadData over now that we've populated it
throw
; do what we did for the message hook function for the ThreadFunc
if (!(pRemoteThreadFunc := DllCall("VirtualAllocEx", "Ptr", hProcess, "Ptr", 0, "Ptr", cbThreadRoutine, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")))
throw
if (!DllCall("WriteProcessMemory", "Ptr", hProcess, "Ptr", pRemoteThreadFunc, "Ptr", pThreadRoutine, "Ptr", cbThreadRoutine, "Ptr", 0))
throw
; start the thread in the remote process
hRemoteThread := DllCall("CreateRemoteThread", "Ptr", hProcess, "Ptr", 0, "Ptr", 0, "Ptr", pRemoteThreadFunc, "Ptr", pRemoteThreadData, "UInt", 0, "Ptr", 0, "Ptr")
if (hRemoteThread) {
Progress m b, Waiting for remote thread to finish,,, Tahoma
Progress 99
loop
r := DllCall(MsgWaitForMultipleObjectsEx, "uint", 1, "ptr*", hRemoteThread, "uint", INFINITE, "uint", 0x4FF, "uint", 0x6), Sleep -1 ; stolen from Lexikos
until (r == 0 || r == -1)
Progress Off
if (DllCall("GetExitCodeThread", "Ptr", hRemoteThread, "UInt*", exitCode)) { ; get the number the ThreadFunc returned
if (exitCode == 1) { ; if our thread exited normally
if (DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", pRemoteThreadData+A_PtrSize+(16*4)+4, "Ptr*", allocatedSize, "Ptr", A_PtrSize, "Ptr*", br) && br == A_PtrSize && allocatedSize) { ; get the size of the filename string
if (DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", pRemoteThreadData+A_PtrSize+(16*4)+4+A_PtrSize, "Ptr*", lpSelectedFilenames, "Ptr", A_PtrSize, "Ptr*", br) && br == A_PtrSize && lpSelectedFilenames) { ; get the address of where the filename string is stored
VarSetCapacity(filenamesBuf, allocatedSize) ; allocate space for our copy of it
if (DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", lpSelectedFilenames, "Ptr", &filenamesBuf, "Ptr", allocatedSize, "Ptr*", br) && br == allocatedSize) { ; read the filename string from the remote process and copy it over
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", pRemoteThreadData+A_PtrSize+(16*4), "Int*", openFolderPathInstead, "Ptr", 4, "Ptr", 0)
fileNames := &filenamesBuf
while (*fileNames) { ; keep reading the filenames until we reach a double-null terminated one: https://blogs.msdn.microsoft.com/oldnewthing/20091008-00?p=16443
MsgBox % (openFolderPathInstead ? "Open folder path:`r`n" : "") . (filename := StrGet(fileNames, MAX_PATH, "UTF-16")) ; and if you see "Open folder path" more than once, something went wrong
fileNames += (DllCall("ntdll\wcslen", "Ptr", fileNames, "CDecl Ptr") * 2) + 2
}
}
}
}
}
}
} else throw
} ;catch {}
finally {
if (cleanup) {
if (hProcess) {
if (lpSelectedFilenames) ; free the memory the injected function allocated to store the strings
DllCall("VirtualFreeEx", "Ptr", hProcess, "Ptr", lpSelectedFilenames, "Ptr", 0, "UInt", MEM_RELEASE := 0x8000)
if (hRemoteThread)
DllCall("CloseHandle", "Ptr", hRemoteThread)
if (pRemoteThreadFunc)
DllCall("VirtualFreeEx", "Ptr", hProcess, "Ptr", pRemoteThreadFunc, "Ptr", 0, "UInt", MEM_RELEASE)
if (pGetMsgFunc)
DllCall("VirtualFreeEx", "Ptr", hProcess, "Ptr", pGetMsgFunc, "Ptr", 0, "UInt", MEM_RELEASE)
if (pRemoteThreadData)
DllCall("VirtualFreeEx", "Ptr", hProcess, "Ptr", pRemoteThreadData, "Ptr", 0, "UInt", MEM_RELEASE)
; close our handle to the process
DllCall("CloseHandle", "Ptr", hProcess)
}
if (pCompiled)
DllCall("GlobalFree", "Ptr", pCompiled, "Ptr") ; free memory for compiled code
if (IsObject(ctx))
TccCleanup(ctx) ; free libtcc context and unload libtcc.dll
}
}
}
CompileC(codeToCompile, outputToMemory := True, performRelocation := True, tcc_dir := "")
{
static ptrErrCb := 0, TCC_RELOCATE_AUTO := 1, orrrr_clic := 270
; if we're called as a result of libtcc's error callback, then:
if ((ptrErrCb) && ((x := A_EventInfo == ptrErrCb) || codeToCompile == orrrr_clic)) {
if (codeToCompile == orrrr_clic && x && outputToMemory)
MsgBox % StrGet(outputToMemory, "CP0") ; show the error message
return
}
if (!ptrErrCb)
ptrErrCb := RegisterCallback(A_ThisFunc, "Fast CDecl", 2)
if (!tcc_dir)
tcc_dir := A_ScriptDir
if ((libtcc := DllCall("LoadLibrary", "Str", tcc_dir . "\libtcc.dll", "Ptr"))) {
ctx := {hModuleTcc: libtcc}
if ((s := DllCall("libtcc\tcc_new", "CDecl Ptr"))) {
success := False
DllCall("libtcc\tcc_set_error_func", "Ptr", s, "Ptr", orrrr_clic, "Ptr", ptrErrCb, "CDecl")
DllCall("libtcc\tcc_set_output_type", "Ptr", s, "Int", outputToMemory ? 0 : 3, "CDecl")
DllCall("libtcc\tcc_set_options", "Ptr", s, "AStr", "-nostdinc -nostdlib", "CDecl") ; since all we have is libtcc.dll, don't look for include files we don't have or libtcc1.a
if (DllCall("libtcc\tcc_compile_string", "Ptr", s, "AStr", codeToCompile, "CDecl") == 0) {
if (outputToMemory) {
success := performRelocation ? DllCall("libtcc\tcc_relocate", "Ptr", s, "Ptr", TCC_RELOCATE_AUTO, "CDecl") == 0 : True
} else {
success := DllCall("libtcc\tcc_output_file", "Ptr", s, "AStr", "test.obj", "CDecl") == 0
}
}
ctx := {hModuleTcc: libtcc, tccState: s}
if (success && outputToMemory)
return ctx
}
TccCleanup(ctx)
} else {
dw := A_LastError
ccherrFmt := DllCall("FormatMessage", "UInt", 0x00000100 | 0x00001000 | 0x00000200, "Ptr", 0, "UInt", dw, "UInt", 1024, "Ptr*", errFmt, "UInt", 0, "Ptr", 0, "UInt")
err := "libtcc was unable to be loaded: " . (ccherrFmt ? StrGet(errFmt, ccherrFmt) : "") . " (" . dw . ")"
if (ccherrFmt)
DllCall("kernel32\LocalFree", "Ptr", errFmt, "Ptr")
throw err
}
return 0
}
TccCleanup(tccContext)
{
if (tccContext) {
; if (tccContext.ptrErrCb) {
; DllCall("GlobalFree", "Ptr", tccContext.ptrErrCb, "Ptr")
; tccContext.ptrErrCb := 0
; }
if (tccContext.tccState) {
DllCall("libtcc\tcc_delete", "Ptr", tccContext.tccState, "CDecl")
tccContext.tccState := 0
}
if (tccContext.hModuleTcc) {
DllCall("FreeLibrary", "Ptr", tccContext.hModuleTcc)
tccContext.hModuleTcc := 0
}
}
}
; Very little error checking. TBH, I'd be surprised if someone actually uses this, so...
ProcAddressFromRemoteProcess(hProcess, hModule, targetFuncName, ByRef Magic := 0)
{
; MarkHC: https://www.unknowncheats.me/forum/1457119-post3.html
IMAGE_DOS_SIGNATURE := 0x5A4D, IMAGE_NT_SIGNATURE := 0x4550
if (DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule, "UShort*", header, "Ptr", 2, "Ptr*", br) && br == 2 && header == IMAGE_DOS_SIGNATURE) {
if (DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+60, "Int*", e_lfanew, "Ptr", 4, "Ptr*", br) && DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+e_lfanew, "UInt*", Signature, "Ptr", 4, "Ptr*", br)) {
if (Signature == IMAGE_NT_SIGNATURE) {
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+e_lfanew+24, "UShort*", Magic, "Ptr", 2, "Ptr*", br)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+e_lfanew+24+(Magic == (IMAGE_NT_OPTIONAL_HDR64_MAGIC := 0x20b) ? 112 : 96), "UInt*", exportTableRVA, "Ptr", 4, "Ptr*", br)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+exportTableRVA+20, "UInt*", NumberOfFunctions, "Ptr", 4, "Ptr*", br)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+exportTableRVA+24, "UInt*", NumberOfNames, "Ptr", 4, "Ptr*", br)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+exportTableRVA+28, "UInt*", AddressOfFunctions, "Ptr", 4, "Ptr*", br)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+exportTableRVA+32, "UInt*", AddressOfNames, "Ptr", 4, "Ptr*", br)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+exportTableRVA+36, "UInt*", AddressOfNameOrdinals, "Ptr", 4, "Ptr*", br)
VarSetCapacity(functions, NumberOfFunctions * 4)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+AddressOfFunctions, "Ptr", &functions, "Ptr", NumberOfFunctions * 4, "Ptr*", br)
VarSetCapacity(exports, NumberOfNames * 4)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+AddressOfNames, "Ptr", &exports, "Ptr", NumberOfNames * 4, "Ptr*", br)
VarSetCapacity(ordinals, NumberOfNames * 2)
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+AddressOfNameOrdinals, "Ptr", &ordinals, "Ptr", NumberOfNames * 2, "Ptr*", br)
Loop % NumberOfNames {
addr := NumGet(exports, 4 * (A_Index - 1), "UInt")
i := 0, funcName := ""
while (true) {
DllCall("ReadProcessMemory", "Ptr", hProcess, "Ptr", hModule+addr+i, "Int*", letter, "Ptr", 1, "Ptr*", br)
if (!letter)
break
funcName .= Chr(letter)
i += 1
}
if (funcName == targetFuncName) {
ordinal := NumGet(ordinals, 2 * (A_Index - 1), "UShort")
return NumGet(functions, 4 * ordinal, "UInt") + hModule
}
}
}
}
}
return 0
}
If nothing's selected, the path of the folder is returned instead; else, you should hopefully get back the full paths of selected items. You can force the retrieval of the folder path regardless of whether anything is selected by uncommenting the "NumPut(True" line.
I tried in WIndows 7 64-bit SP1 on a CID open in Chrome and I was successful on my first attempt: