WCHAR *windowTextAndLen(HWND hwnd, LRESULT *len) { LRESULT n; WCHAR *text; n = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); if (len != NULL) *len = n; // WM_GETTEXTLENGTH does not include the null terminator text = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); // note the comparison: the size includes the null terminator, but the return does not if (GetWindowTextW(hwnd, text, n + 1) != n) { logLastError(L"error getting window text"); // on error, return an empty string to be safe *text = L'\0'; if (len != NULL) *len = 0; } return text; }
HWND uiWindowsEnsureCreateControlHWND(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, HINSTANCE hInstance, LPVOID lpParam, BOOL useStandardControlFont) { HWND hwnd; // don't let using the arrow keys in a uiRadioButtons leave the radio buttons if ((dwStyle & WS_TABSTOP) != 0) dwStyle |= WS_GROUP; hwnd = CreateWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle | WS_CHILD | WS_VISIBLE, 0, 0, // use a nonzero initial size just in case some control breaks with a zero initial size 100, 100, utilWindow, NULL, hInstance, lpParam); if (hwnd == NULL) logLastError("error creating window in uiWindowsUtilCreateControlHWND()"); if (useStandardControlFont) SendMessageW(hwnd, WM_SETFONT, (WPARAM) hMessageFont, (LPARAM) TRUE); return hwnd; }
uiGroup *uiNewGroup(const char *text) { uiGroup *g; WCHAR *wtext; uiWindowsNewControl(uiGroup, g); wtext = toUTF16(text); g->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CONTROLPARENT, L"button", wtext, BS_GROUPBOX, hInstance, NULL, TRUE); uiFree(wtext); if (SetWindowSubclass(g->hwnd, groupSubProc, 0, (DWORD_PTR) g) == FALSE) logLastError(L"error subclassing groupbox to handle parent messages"); return g; }
/* Windows doesn't natively support mouse crossing events. TrackMouseEvent() (and its comctl32.dll wrapper _TrackMouseEvent()) both allow for a window to receive the WM_MOUSELEAVE message when the mouse leaves the client area. There's no equivalent WM_MOUSEENTER because it can be simulated (https://blogs.msdn.microsoft.com/oldnewthing/20031013-00/?p=42193). Unfortunately, WM_MOUSELEAVE does not get generated while the mouse is captured. We need to capture for drag behavior to work properly, so this isn't going to mix well. So what we do: - on WM_MOUSEMOVE, if we don't have the capture, start tracking - this will handle the case of the capture being released while still in the area - on WM_MOUSELEAVE, mark that we are no longer tracking - Windows has already done the work of that for us; it's just a flag we use for the next part - when starting capture, stop tracking if we are tracking - if capturing, manually check if the pointer is in the client rect on each area event */ static void track(uiArea *a, BOOL tracking) { TRACKMOUSEEVENT tm; // do nothing if there's no change if (a->tracking && tracking) return; if (!a->tracking && !tracking) return; a->tracking = tracking; ZeroMemory(&tm, sizeof (TRACKMOUSEEVENT)); tm.cbSize = sizeof (TRACKMOUSEEVENT); tm.dwFlags = TME_LEAVE; if (!a->tracking) tm.dwFlags |= TME_CANCEL; tm.hwndTrack = a->hwnd; if (_TrackMouseEvent(&tm) == 0) logLastError(L"error setting up mouse tracking"); }
HRESULT uiprivTableFinishEditingText(uiTable *t) { uiprivTableColumnParams *p; uiTableValue *value; char *text; if (t->edit == NULL) return S_OK; text = uiWindowsWindowText(t->edit); value = uiNewTableValueString(text); uiFreeText(text); p = (*(t->columns))[t->editedSubitem]; uiprivTableModelSetCellValue(t->model, t->editedItem, p->textModelColumn, value); uiFreeTableValue(value); // always refresh the value in case the model rejected it if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (t->editedItem), 0) == (LRESULT) (-1)) { logLastError(L"LVM_UPDATE"); return E_FAIL; } return uiprivTableAbortEditingText(t); }
static void wheelscroll(uiArea *a, int which, struct scrollParams *p, WPARAM wParam, LPARAM lParam) { int delta; int lines; UINT scrollAmount; delta = GET_WHEEL_DELTA_WPARAM(wParam); if (SystemParametersInfoW(p->wheelSPIAction, 0, &scrollAmount, 0) == 0) // TODO use scrollAmount == 3 (for both v and h) instead? logLastError(L"error getting area wheel scroll amount"); if (scrollAmount == WHEEL_PAGESCROLL) scrollAmount = p->pagesize; if (scrollAmount == 0) // no mouse wheel scrolling (or t->pagesize == 0) return; // the rest of this is basically http://blogs.msdn.com/b/oldnewthing/archive/2003/08/07/54615.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/08/11/54624.aspx // see those pages for information on subtleties delta += *(p->wheelCarry); lines = delta * ((int) scrollAmount) / WHEEL_DELTA; *(p->wheelCarry) = delta - lines * WHEEL_DELTA / ((int) scrollAmount); scrollby(a, which, p, -lines); }
static void appendMenuItem(HMENU menu, uiMenuItem *item) { UINT uFlags; uFlags = MF_SEPARATOR; if (item->type != typeSeparator) { uFlags = MF_STRING; if (item->disabled) uFlags |= MF_DISABLED | MF_GRAYED; if (item->checked) uFlags |= MF_CHECKED; } if (AppendMenuW(menu, uFlags, item->id, item->name) == 0) logLastError(L"error appending menu item"); if (item->len >= item->cap) { item->cap += grow; item->hmenus = (HMENU *) uiRealloc(item->hmenus, item->cap * sizeof (HMENU), "HMENU[]"); } item->hmenus[item->len] = menu; item->len++; }
void uiMain(void) { MSG msg; int res; HWND active, focus; for (;;) { SetLastError(0); res = GetMessageW(&msg, NULL, 0, 0); if (res < 0) logLastError("error calling GetMessage() in uiMain()"); if (res == 0) // WM_QUIT break; active = GetActiveWindow(); if (active == NULL) { msgloop_else(&msg); continue; } // bit of logic involved here: // we don't want dialog messages passed into Areas, so we don't call IsDialogMessageW() there // as for Tabs, we can't have both WS_TABSTOP and WS_EX_CONTROLPARENT set at the same time, so we hotswap the two styles to get the behavior we want focus = GetFocus(); if (focus != NULL) { switch (windowClassOf(focus, L"TODO Area not yet implemented", NULL)) { case 0: // uiArea // msgloop_area(active, focus, &msg); continue; } // else fall through } if (IsDialogMessage(active, &msg) != 0) continue; msgloop_else(&msg); } }
uiWindowsSizing *uiWindowsNewSizing(HWND hwnd) { uiWindowsSizing *d; HDC dc; HFONT prevfont; TEXTMETRICW tm; SIZE size; d = uiNew(uiWindowsSizing); dc = GetDC(hwnd); if (dc == NULL) logLastError("error getting DC in uiWindowsNewSizing()"); prevfont = (HFONT) SelectObject(dc, hMessageFont); if (prevfont == NULL) logLastError("error loading control font into device context in uiWindowsNewSizing()"); ZeroMemory(&tm, sizeof (TEXTMETRICW)); if (GetTextMetricsW(dc, &tm) == 0) logLastError("error getting text metrics in uiWindowsNewSizing()"); if (GetTextExtentPoint32W(dc, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size) == 0) logLastError("error getting text extent point in uiWindowsNewSizing()"); d->BaseX = (int) ((size.cx / 26 + 1) / 2); d->BaseY = (int) tm.tmHeight; d->InternalLeading = tm.tmInternalLeading; if (SelectObject(dc, prevfont) != hMessageFont) logLastError("error restoring previous font into device context in uiWindowsSizing()"); if (ReleaseDC(hwnd, dc) == 0) logLastError("error releasing DC in uiWindowsSizing()"); d->XPadding = uiWindowsDlgUnitsToX(winXPadding, d->BaseX); d->YPadding = uiWindowsDlgUnitsToY(winYPadding, d->BaseY); return d; }
static void areaMouseEvent(uiArea *a, uintmax_t down, uintmax_t up, WPARAM wParam, LPARAM lParam) { uiAreaMouseEvent me; uintmax_t button; RECT r; me.X = GET_X_LPARAM(lParam); me.Y = GET_Y_LPARAM(lParam); if (GetClientRect(a->hwnd, &r) == 0) logLastError("error getting client rect of area in areaMouseEvent()"); me.ClientWidth = r.right - r.left; me.ClientHeight = r.bottom - r.top; me.HScrollPos = a->hscrollpos; me.VScrollPos = a->vscrollpos; me.Down = down; me.Up = up; me.Count = 0; if (me.Down != 0) // GetMessageTime() returns LONG and GetDoubleClckTime() returns UINT, which are int32 and uint32, respectively, but we don't need to worry about the signedness because for the same bit widths and two's complement arithmetic, s1-s2 == u1-u2 if bits(s1)==bits(s2) and bits(u1)==bits(u2) (and Windows requires two's complement: http://blogs.msdn.com/b/oldnewthing/archive/2005/05/27/422551.aspx) // signedness isn't much of an issue for these calls anyway because http://stackoverflow.com/questions/24022225/what-are-the-sign-extension-rules-for-calling-windows-api-functions-stdcall-t and that we're only using unsigned values (think back to how you (didn't) handle signedness in assembly language) AND because of the above AND because the statistics below (time interval and width/height) really don't make sense if negative me.Count = clickCounterClick(&(a->cc), me.Down, me.X, me.Y, GetMessageTime(), GetDoubleClickTime(), GetSystemMetrics(SM_CXDOUBLECLK) / 2, GetSystemMetrics(SM_CYDOUBLECLK) / 2); // though wparam will contain control and shift state, let's just one function to get modifiers for both keyboard and mouse events; it'll work the same anyway since we have to do this for alt and windows key (super) me.Modifiers = getModifiers(); button = me.Down; if (button == 0) button = me.Up; me.Held1To64 = 0; if (button != 1 && (wParam & MK_LBUTTON) != 0) me.Held1To64 |= 1 << 0; if (button != 2 && (wParam & MK_MBUTTON) != 0) me.Held1To64 |= 1 << 1; if (button != 3 && (wParam & MK_RBUTTON) != 0) me.Held1To64 |= 1 << 2; if (button != 4 && (wParam & MK_XBUTTON1) != 0) me.Held1To64 |= 1 << 3; if (button != 5 && (wParam & MK_XBUTTON2) != 0) me.Held1To64 |= 1 << 4; // on Windows, we have to capture on drag ourselves if (me.Down != 0 && !a->capturing) { SetCapture(a->hwnd); a->capturing = TRUE; } // only release capture when all buttons released if (me.Up != 0 && a->capturing && me.Held1To64 == 0) { // unset flag first as ReleaseCapture() sends WM_CAPTURECHANGED a->capturing = FALSE; if (ReleaseCapture() == 0) logLastError("error releasing capture on drag in areaMouseEvent()"); } (*(a->ah->MouseEvent))(a->ah, a, &me); }
static void endColorDialog(struct colorDialog *c, INT_PTR code) { if (EndDialog(c->hwnd, code) == 0) logLastError(L"error ending color dialog"); uiFree(c); }
void setWindowInsertAfter(HWND hwnd, HWND insertAfter) { if (SetWindowPos(hwnd, insertAfter, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE) == 0) logLastError("error reordering window in setWindowInsertAfter()"); }
int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "usage:\n" " inject <dllname.dll> <command> [args] ...\n" " inject <dllname.dll> <process-id>\n" " inject <dllname.dll> !<process-name>\n" ); return 1; } BOOL bAttach = FALSE; DWORD dwProcessId = ~0; if (isNumber(argv[2])) { dwProcessId = atol(argv[2]); bAttach = TRUE; } else if (argv[2][0] == '!') { const char *szProcessName = &argv[2][1]; if (!getProcessIdByName(szProcessName, &dwProcessId)) { fprintf(stderr, "error: failed to find process %s\n", szProcessName); return 1; } bAttach = TRUE; fprintf(stderr, "dwProcessId = %lu\n", dwProcessId); } HANDLE hSemaphore = NULL; const char *szDll = argv[1]; if (!USE_SHARED_MEM) { SetEnvironmentVariableA("INJECT_DLL", szDll); } else { hSemaphore = CreateSemaphore(NULL, 1, 1, "inject_semaphore"); if (hSemaphore == NULL) { fprintf(stderr, "error: failed to create semaphore\n"); return 1; } DWORD dwWait = WaitForSingleObject(hSemaphore, 0); if (dwWait == WAIT_TIMEOUT) { fprintf(stderr, "info: waiting for another inject instance to finish\n"); dwWait = WaitForSingleObject(hSemaphore, INFINITE); } if (dwWait != WAIT_OBJECT_0) { fprintf(stderr, "error: failed to enter semaphore gate\n"); return 1; } SetSharedMem(szDll); } BOOL bAttachDwm = FALSE; PROCESS_INFORMATION processInfo; HANDLE hProcess; if (bAttach) { BOOL bRet; HANDLE hToken = NULL; bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken); if (!bRet) { fprintf(stderr, "error: OpenProcessToken returned %u\n", (unsigned)bRet); return 1; } LUID Luid; bRet = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid); if (!bRet) { fprintf(stderr, "error: LookupPrivilegeValue returned %u\n", (unsigned)bRet); return 1; } TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Luid = Luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof tp, NULL, NULL); if (!bRet) { fprintf(stderr, "error: AdjustTokenPrivileges returned %u\n", (unsigned)bRet); return 1; } DWORD dwDesiredAccess = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_TERMINATE; hProcess = OpenProcess( dwDesiredAccess, FALSE /* bInheritHandle */, dwProcessId); if (!hProcess) { logLastError("failed to open process"); return 1; } char szProcess[MAX_PATH]; DWORD dwRet = GetModuleFileNameEx(hProcess, 0, szProcess, sizeof szProcess); assert(dwRet); if (dwRet && stricmp(getBaseName(szProcess), "dwm.exe") == 0) { bAttachDwm = TRUE; } } else { std::string commandLine; char sep = 0; for (int i = 2; i < argc; ++i) { const char *arg = argv[i]; if (sep) { commandLine.push_back(sep); } if (needsQuote(arg)) { quoteArg(commandLine, arg); } else { commandLine.append(arg); } sep = ' '; } STARTUPINFO startupInfo; memset(&startupInfo, 0, sizeof startupInfo); startupInfo.cb = sizeof startupInfo; // Create the process in suspended state if (!CreateProcessA( NULL, const_cast<char *>(commandLine.c_str()), // only modified by CreateProcessW 0, // process attributes 0, // thread attributes TRUE, // inherit handles CREATE_SUSPENDED, NULL, // environment NULL, // current directory &startupInfo, &processInfo)) { DWORD dwLastError = GetLastError(); fprintf(stderr, "error: failed to execute %s (%lu)\n", commandLine.c_str(), dwLastError); if (dwLastError == ERROR_ELEVATION_REQUIRED) { fprintf(stderr, "error: target program requires elevated priviledges and must be started from an Administrator Command Prompt, or UAC must be disabled\n"); } return 1; } hProcess = processInfo.hProcess; } /* * XXX: Mixed architecture don't quite work. See also * http://www.corsix.org/content/dll-injection-and-wow64 */ { typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL); PFNISWOW64PROCESS pfnIsWow64Process; pfnIsWow64Process = (PFNISWOW64PROCESS) GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process"); if (pfnIsWow64Process) { BOOL isParentWow64 = FALSE; BOOL isChildWow64 = FALSE; if (pfnIsWow64Process(GetCurrentProcess(), &isParentWow64) && pfnIsWow64Process(hProcess, &isChildWow64) && isParentWow64 != isChildWow64) { fprintf(stderr, "error: binaries mismatch: you need to use the " #ifdef _WIN64 "32-bits" #else "64-bits" #endif " apitrace binaries to trace this application\n"); TerminateProcess(hProcess, 1); return 1; } } } if (bAttachDwm && IsWindows8OrGreater()) { // Switch to Microsoft Basic Display Driver before injecting, so that // we don't trace with it. devconDisable(DEVCON_CLASS_DISPLAY); Sleep(1000); } const char *szDllName; szDllName = "injectee.dll"; char szDllPath[MAX_PATH]; GetModuleFileNameA(NULL, szDllPath, sizeof szDllPath); getDirName(szDllPath); strncat(szDllPath, szDllName, sizeof szDllPath - strlen(szDllPath) - 1); #if 1 if (!injectDll(hProcess, szDllPath)) { TerminateProcess(hProcess, 1); return 1; } #endif DWORD exitCode; if (bAttach) { if (bAttachDwm) { restartDwmComposition(hProcess); } exitCode = 0; } else { // Start main process thread ResumeThread(processInfo.hThread); // Wait for it to finish WaitForSingleObject(hProcess, INFINITE); if (pSharedMem && !pSharedMem->bReplaced) { fprintf(stderr, "warning: %s was never used: application probably does not use this API\n", szDll); } exitCode = ~0; GetExitCodeProcess(hProcess, &exitCode); CloseHandle(processInfo.hThread); } CloseHandle(hProcess); if (hSemaphore) { ReleaseSemaphore(hSemaphore, 1, NULL); CloseHandle(hSemaphore); } return (int)exitCode; }
// Force DWM process to recreate all its Direct3D objects. static void restartDwmComposition(HANDLE hProcess) { HRESULT hr; HMODULE hModule = LoadLibraryA("dwmapi"); assert(hModule); if (!hModule) { return; } typedef HRESULT (WINAPI *PFNDWMISCOMPOSITIONENABLED)(BOOL *pfEnabled); PFNDWMISCOMPOSITIONENABLED pfnDwmIsCompositionEnabled = (PFNDWMISCOMPOSITIONENABLED)GetProcAddress(hModule, "DwmIsCompositionEnabled"); assert(pfnDwmIsCompositionEnabled); if (!pfnDwmIsCompositionEnabled) { return; } typedef HRESULT (WINAPI *PFNDWMENABLECOMPOSITION)(UINT uCompositionAction); PFNDWMENABLECOMPOSITION pfnDwmEnableComposition = (PFNDWMENABLECOMPOSITION)GetProcAddress(hModule, "DwmEnableComposition"); assert(pfnDwmEnableComposition); if (!pfnDwmEnableComposition) { return; } BOOL bIsWindows8OrGreater = IsWindows8OrGreater(); if (bIsWindows8OrGreater) { // Windows 8 ignores DwmEnableComposition(DWM_EC_DISABLECOMPOSITION). // It is however possible to force DWM to restart by restarting the // display device via the devcon utility devconEnable(DEVCON_CLASS_DISPLAY); } else { BOOL fEnabled = FALSE; hr = pfnDwmIsCompositionEnabled(&fEnabled); if (FAILED(hr) || !fEnabled) { return; } fprintf(stderr, "info: restarting DWM composition\n"); hr = pfnDwmEnableComposition(DWM_EC_DISABLECOMPOSITION); assert(SUCCEEDED(hr)); if (FAILED(hr)) { return; } Sleep(1000/30); hr = pfnDwmEnableComposition(DWM_EC_ENABLECOMPOSITION); assert(SUCCEEDED(hr)); (void)hr; } fprintf(stderr, "Press any key when finished tracing\n"); getchar(); DWORD dwExitCode; if (GetExitCodeProcess(hProcess, &dwExitCode) && dwExitCode != STILL_ACTIVE) { // DWM process has already terminated return; } fprintf(stderr, "info: restarting DWM process\n"); if (bIsWindows8OrGreater) { // From Windows 8 onwards DWM no longer runs as a service. We just // kill it and winlogon parent process will respawn it. if (!TerminateProcess(hProcess, 0)) { logLastError("failed to terminate DWM process"); } } else { hr = pfnDwmEnableComposition(DWM_EC_DISABLECOMPOSITION); assert(SUCCEEDED(hr)); restartService("uxsms"); } }
static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) { uiTableValue *value; WCHAR *wstr; bool enabled; HTHEME theme; RECT r; TEXTMETRICW tm; if (hr != S_OK) return hr; if (s->p->buttonModelColumn == -1) return S_OK; value = uiprivTableModelCellValue(s->model, s->iItem, s->p->buttonModelColumn); wstr = toUTF16(uiTableValueString(value)); uiFreeTableValue(value); enabled = uiprivTableModelCellEditable(s->model, s->iItem, s->p->buttonClickableModelColumn); theme = OpenThemeData(s->t->hwnd, L"button"); if (GetTextMetricsW(s->dc, &tm) == 0) { logLastError(L"GetTextMetricsW()"); hr = E_FAIL; goto fail; } r = s->m->subitemBounds; if (theme != NULL) { int state; state = PBS_NORMAL; if (!enabled) state = PBS_DISABLED; hr = DrawThemeBackground(theme, s->dc, BP_PUSHBUTTON, state, &r, NULL); if (hr != S_OK) { logHRESULT(L"DrawThemeBackground()", hr); goto fail; } // TODO DT_EDITCONTROL? // TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here // TODO is there a theme property we can get instead of hardcoding these flags? if not, make these flags a macro hr = DrawThemeText(theme, s->dc, BP_PUSHBUTTON, state, wstr, -1, DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX, 0, &r); if (hr != S_OK) { logHRESULT(L"DrawThemeText()", hr); goto fail; } } else { UINT state; HBRUSH color, prevColor; int prevBkMode; // TODO check errors // TODO explain why we're not doing this in the themed case (it has to do with extra transparent pixels) InflateRect(&r, -1, -1); state = DFCS_BUTTONPUSH; if (!enabled) state |= DFCS_INACTIVE; if (DrawFrameControl(s->dc, &r, DFC_BUTTON, state) == 0) { logLastError(L"DrawFrameControl()"); hr = E_FAIL; goto fail; } color = GetSysColorBrush(COLOR_BTNTEXT); // TODO check errors for these two prevColor = (HBRUSH) SelectObject(s->dc, color); prevBkMode = SetBkMode(s->dc, TRANSPARENT); // TODO DT_EDITCONTROL? // TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here if (DrawTextW(s->dc, wstr, -1, &r, DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX) == 0) { logLastError(L"DrawTextW()"); hr = E_FAIL; goto fail; } // TODO check errors for these two SetBkMode(s->dc, prevBkMode); SelectObject(s->dc, prevColor); } hr = S_OK; fail: // TODO check errors if (theme != NULL) CloseThemeData(theme); uiprivFree(wstr); return hr; }
static void cbSetItemData(HWND cb, WPARAM item, LPARAM data) { if (SendMessageW(cb, CB_SETITEMDATA, item, data) == (LRESULT) CB_ERR) logLastError(L"error setting combobox item data"); }
void setWindowText(HWND hwnd, WCHAR *wtext) { if (SetWindowTextW(hwnd, wtext) == 0) logLastError(L"error setting window text"); }
HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult) { LVHITTESTINFO ht; uiprivTableColumnParams *p; int modelColumn, editableColumn; bool text, checkbox; uiTableValue *value; int checked, editable; HRESULT hr; ZeroMemory(&ht, sizeof (LVHITTESTINFO)); ht.pt = nm->ptAction; if (SendMessageW(t->hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM) (&ht)) == (LRESULT) (-1)) goto done; modelColumn = -1; editableColumn = -1; text = false; checkbox = false; p = (*(t->columns))[ht.iSubItem]; if (p->textModelColumn != -1) { modelColumn = p->textModelColumn; editableColumn = p->textEditableModelColumn; text = true; } else if (p->checkboxModelColumn != -1) { modelColumn = p->checkboxModelColumn; editableColumn = p->checkboxEditableModelColumn; checkbox = true; } else if (p->buttonModelColumn != -1) { modelColumn = p->buttonModelColumn; editableColumn = p->buttonClickableModelColumn; } if (modelColumn == -1) goto done; if (text && t->inDoubleClickTimer) // don't even ask for info if it's too soon to edit text goto done; if (!uiprivTableModelCellEditable(t->model, ht.iItem, editableColumn)) goto done; if (text) { hr = openEditControl(t, ht.iItem, ht.iSubItem, p); if (hr != S_OK) return hr; } else if (checkbox) { if ((ht.flags & LVHT_ONITEMICON) == 0) goto done; value = uiprivTableModelCellValue(t->model, ht.iItem, modelColumn); checked = uiTableValueInt(value); uiFreeTableValue(value); value = uiNewTableValueInt(!checked); uiprivTableModelSetCellValue(t->model, ht.iItem, modelColumn, value); uiFreeTableValue(value); } else uiprivTableModelSetCellValue(t->model, ht.iItem, modelColumn, NULL); // always refresh the value in case the model rejected it if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (ht.iItem), 0) == (LRESULT) (-1)) { logLastError(L"LVM_UPDATE"); return E_FAIL; } done: *lResult = 0; return S_OK; }
static BOOL ejectDll(HANDLE hProcess, const char *szDllPath) { /* * Enumerate all modules. */ HMODULE *phModules = NULL; DWORD cb = sizeof *phModules * #ifdef NDEBUG 32 #else 4 #endif ; DWORD cbNeeded = 0; while (true) { phModules = (HMODULE *)realloc(phModules, cb); if (!EnumProcessModules(hProcess, phModules, cb, &cbNeeded)) { logLastError("failed to enumerate modules in remote process"); free(phModules); return FALSE; } if (cbNeeded < cb) { break; } cb *= 2; } DWORD cNumModules = cbNeeded / sizeof *phModules; /* * Search our DLL. */ const char *szDllName = getBaseName(szDllPath); HMODULE hModule = NULL; for (unsigned i = 0; i < cNumModules; ++i) { char szModName[MAX_PATH]; if (GetModuleFileNameExA(hProcess, phModules[i], szModName, ARRAY_SIZE(szModName))) { if (stricmp(getBaseName(szModName), szDllName) == 0) { hModule = phModules[i]; break; } } } free(phModules); if (!hModule) { debugPrintf("inject: error: failed to find %s module in the remote process\n", szDllName); return FALSE; } PTHREAD_START_ROUTINE lpStartAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("KERNEL32"), "FreeLibrary"); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, hModule, 0, NULL); if (!hThread) { logLastError("failed to create remote thread"); return FALSE; } WaitForSingleObject(hThread, INFINITE); DWORD bRet = 0; GetExitCodeThread(hThread, &bRet); if (!bRet) { debugPrintf("inject: error: failed to unload %s from the remote process\n", szDllPath); return FALSE; } return TRUE; }
static void areaMouseEvent(uiArea *a, uintmax_t down, uintmax_t up, WPARAM wParam, LPARAM lParam) { uiAreaMouseEvent me; uintmax_t button; POINT clientpt; RECT client; BOOL inClient; double xpix, ypix; if (a->capturing) { clientpt.x = GET_X_LPARAM(lParam); clientpt.y = GET_Y_LPARAM(lParam); if (GetClientRect(a->hwnd, &client) == 0) logLastError("TODO"); inClient = PtInRect(&client, clientpt); if (inClient && !a->inside) { a->inside = TRUE; (*(a->ah->MouseCrossed))(a->ah, a, 0); clickCounterReset(&(a->cc)); } else if (!inClient && a->inside) { a->inside = FALSE; (*(a->ah->MouseCrossed))(a->ah, a, 1); clickCounterReset(&(a->cc)); } } xpix = (double) GET_X_LPARAM(lParam); ypix = (double) GET_Y_LPARAM(lParam); // these are in pixels; we need points pixelsToDIP(a, &xpix, &ypix); me.X = xpix; me.Y = ypix; if (a->scrolling) { me.X += a->hscrollpos; me.Y += a->vscrollpos; } loadAreaSize(a, NULL, &(me.AreaWidth), &(me.AreaHeight)); me.Down = down; me.Up = up; me.Count = 0; if (me.Down != 0) // GetMessageTime() returns LONG and GetDoubleClckTime() returns UINT, which are int32 and uint32, respectively, but we don't need to worry about the signedness because for the same bit widths and two's complement arithmetic, s1-s2 == u1-u2 if bits(s1)==bits(s2) and bits(u1)==bits(u2) (and Windows requires two's complement: http://blogs.msdn.com/b/oldnewthing/archive/2005/05/27/422551.aspx) // signedness isn't much of an issue for these calls anyway because http://stackoverflow.com/questions/24022225/what-are-the-sign-extension-rules-for-calling-windows-api-functions-stdcall-t and that we're only using unsigned values (think back to how you (didn't) handle signedness in assembly language) AND because of the above AND because the statistics below (time interval and width/height) really don't make sense if negative me.Count = clickCounterClick(&(a->cc), me.Down, me.X, me.Y, GetMessageTime(), GetDoubleClickTime(), GetSystemMetrics(SM_CXDOUBLECLK) / 2, GetSystemMetrics(SM_CYDOUBLECLK) / 2); // though wparam will contain control and shift state, let's just one function to get modifiers for both keyboard and mouse events; it'll work the same anyway since we have to do this for alt and windows key (super) me.Modifiers = getModifiers(); button = me.Down; if (button == 0) button = me.Up; me.Held1To64 = 0; if (button != 1 && (wParam & MK_LBUTTON) != 0) me.Held1To64 |= 1 << 0; if (button != 2 && (wParam & MK_MBUTTON) != 0) me.Held1To64 |= 1 << 1; if (button != 3 && (wParam & MK_RBUTTON) != 0) me.Held1To64 |= 1 << 2; if (button != 4 && (wParam & MK_XBUTTON1) != 0) me.Held1To64 |= 1 << 3; if (button != 5 && (wParam & MK_XBUTTON2) != 0) me.Held1To64 |= 1 << 4; // on Windows, we have to capture on drag ourselves if (me.Down != 0) capture(a, TRUE); // only release capture when all buttons released if (me.Up != 0 && me.Held1To64 == 0) capture(a, FALSE); (*(a->ah->MouseEvent))(a->ah, a, &me); }
static void cbSetCurSel(HWND cb, WPARAM item) { if (SendMessageW(cb, CB_SETCURSEL, item, 0) != (LRESULT) item) logLastError(L"error selecting combobox item"); }
void unregisterWindowClass(void) { if (UnregisterClassW(windowClass, hInstance) == 0) logLastError(L"error unregistering uiWindow window class"); }
static HRESULT drawProgressBarPart(HRESULT hr, struct drawState *s) { int progress; LONG indeterminatePos; HTHEME theme; RECT r; RECT rBorder, rFill[2]; int i, nFill; TEXTMETRICW tm; int sysColor; if (hr != S_OK) return hr; if (s->p->progressBarModelColumn == -1) return S_OK; progress = uiprivTableProgress(s->t, s->iItem, s->iSubItem, s->p->progressBarModelColumn, &indeterminatePos); theme = OpenThemeData(s->t->hwnd, L"PROGRESS"); if (GetTextMetricsW(s->dc, &tm) == 0) { logLastError(L"GetTextMetricsW()"); hr = E_FAIL; goto fail; } r = s->m->subitemBounds; // this sets the height of the progressbar and vertically centers it in one fell swoop r.top += (r.bottom - tm.tmHeight - r.top) / 2; r.bottom = r.top + tm.tmHeight; // TODO check errors rBorder = r; InflateRect(&rBorder, -1, -1); if (theme != NULL) { RECT crect; hr = GetThemeBackgroundContentRect(theme, s->dc, PP_TRANSPARENTBAR, PBBS_NORMAL, &rBorder, &crect); if (hr != S_OK) { logHRESULT(L"GetThemeBackgroundContentRect()", hr); goto fail; } hr = DrawThemeBackground(theme, s->dc, PP_TRANSPARENTBAR, PBBS_NORMAL, &crect, NULL); if (hr != S_OK) { logHRESULT(L"DrawThemeBackground() border", hr); goto fail; } } else { HPEN pen, prevPen; HBRUSH brush, prevBrush; sysColor = COLOR_HIGHLIGHT; if (s->m->selected) sysColor = COLOR_HIGHLIGHTTEXT; // TODO check errors everywhere pen = CreatePen(PS_SOLID, 1, GetSysColor(sysColor)); prevPen = (HPEN) SelectObject(s->dc, pen); brush = (HBRUSH) GetStockObject(NULL_BRUSH); prevBrush = (HBRUSH) SelectObject(s->dc, brush); Rectangle(s->dc, rBorder.left, rBorder.top, rBorder.right, rBorder.bottom); SelectObject(s->dc, prevBrush); SelectObject(s->dc, prevPen); DeleteObject(pen); } nFill = 1; rFill[0] = r; // TODO check error InflateRect(&rFill[0], -1, -1); if (progress != -1) rFill[0].right -= (rFill[0].right - rFill[0].left) * (100 - progress) / 100; else { LONG barWidth; LONG pieceWidth; // TODO explain all this // TODO this should really start the progressbar scrolling into view instead of already on screen when first set rFill[1] = rFill[0]; // save in case we need it barWidth = rFill[0].right - rFill[0].left; pieceWidth = barWidth / indeterminateSegments; rFill[0].left += indeterminatePos % barWidth; if ((rFill[0].left + pieceWidth) >= rFill[0].right) { // make this piece wrap back around nFill++; rFill[1].right = rFill[1].left + (pieceWidth - (rFill[0].right - rFill[0].left)); } else rFill[0].right = rFill[0].left + pieceWidth; } for (i = 0; i < nFill; i++) if (theme != NULL) { hr = DrawThemeBackground(theme, s->dc, PP_FILL, PBFS_NORMAL, &rFill[i], NULL); if (hr != S_OK) { logHRESULT(L"DrawThemeBackground() fill", hr); goto fail; } } else // TODO check errors FillRect(s->dc, &rFill[i], GetSysColorBrush(sysColor)); hr = S_OK; fail: // TODO check errors if (theme != NULL) CloseThemeData(theme); return hr; }