this tool allows to do a few basic operations on a Wine session: - list all running processes - kill a given process - list all threads from a process - suspend/resume a thread - list all (loaded) modules from a process - list all debug channel from a process - change any debug channel from a (running process)
it comes in two flavors:
- command line (run wineproc help to get an idea of the options)
- visual (requires a graphical driver in wine, like X11), with (I hope) an understandable UI
help is required to: - get some decent icons for toolbar (I don't feel like a graphic designer) - <put here what you want>
downside: the get_symbol function in system.c is really ugly. I wouldn't be surprised if Alexandre doesn't like it (I don't like it myself). A cleaner solution would be to implement dbghelp.dll for this kind of behavior (easier said than done).
any comment welcomed!!
(thanks to Dimi for his quick fixies on the listview control BTW)
A+ -- Eric Pouech
Name: wp ChangeLog: Creation of wineproc - a tool for browsing wine processes License: X11 GenDate: 2003/09/03 19:44:49 UTC ModifiedFiles: configure.ac programs/Makefile.in AddedFiles: programs/wineproc/Makefile.in programs/wineproc/splitter.c programs/wineproc/splitter.h programs/wineproc/system.c programs/wineproc/visual.c programs/wineproc/wineproc.c programs/wineproc/wineproc_En.rc programs/wineproc/wineproc.h programs/wineproc/wineproc.rc programs/wineproc/wineprocres.h =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/configure.ac,v retrieving revision 1.176 diff -u -u -r1.176 configure.ac --- configure.ac 3 Sep 2003 00:26:08 -0000 1.176 +++ configure.ac 3 Sep 2003 17:00:54 -0000 @@ -1571,9 +1572,10 @@ programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile +programs/wineproc/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile Index: programs/Makefile.in =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/programs/Makefile.in,v retrieving revision 1.39 diff -u -u -r1.39 Makefile.in --- programs/Makefile.in 23 Jun 2003 19:51:21 -0000 1.39 +++ programs/Makefile.in 24 Jun 2003 17:19:31 -0000 @@ -31,9 +31,10 @@ winemenubuilder \ winemine \ winepath \ + wineproc \ winevdm \ winhelp \ winver # Sub-directories to run make install into INSTALLSUBDIRS = \ @@ -58,6 +63,7 @@ winemenubuilder \ winemine \ winepath \ + wineproc \ winevdm \ winhelp \ winver @@ -78,6 +84,7 @@ winefile \ winemine \ winepath \ + wineproc \ winhelp # Symlinks to apps that we want to run from inside the source tree @@ -87,6 +94,7 @@ wineconsole.exe \ winedbg.exe \ winemenubuilder.exe \ + wineproc.exe \ winevdm.exe \ winhelp.exe @@ -148,6 +156,9 @@ winemenubuilder.exe$(DLLEXT): winemenubuilder/winemenubuilder.exe$(DLLEXT) $(RM) $@ && $(LN_S) winemenubuilder/winemenubuilder.exe$(DLLEXT) $@ +wineproc.exe$(DLLEXT): wineproc/wineproc.exe$(DLLEXT) + $(RM) $@ && $(LN_S) wineproc/wineproc.exe$(DLLEXT) $@ + winevdm.exe$(DLLEXT): winevdm/winevdm.exe$(DLLEXT) $(RM) $@ && $(LN_S) winevdm/winevdm.exe$(DLLEXT) $@ @@ -158,6 +169,7 @@ wineconsole/wineconsole.exe$(DLLEXT): wineconsole winedbg/winedbg.exe$(DLLEXT): winedbg winemenubuilder/winemenubuilder.exe$(DLLEXT): winemenubuilder +wineproc/wineproc.exe$(DLLEXT): wineproc winevdm/winevdm.exe$(DLLEXT): winevdm winhelp/winhelp.exe$(DLLEXT): winhelp --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/Makefile.in 2003-08-30 09:34:12.000000000 +0200 @@ -0,0 +1,19 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = wineproc.exe +APPMODE = cui +IMPORTS = advapi32 kernel32 psapi gdi32 user32 comctl32 + +C_SRCS = \ + splitter.c \ + system.c \ + visual.c \ + wineproc.c + +RC_SRCS = wineproc.rc + +@MAKE_PROG_RULES@ + +### Dependencies: --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/splitter.c 2003-08-30 09:45:42.000000000 +0200 @@ -0,0 +1,108 @@ +#include <windows.h> +#include <commctrl.h> +#include "splitter.h" + +/****************************************************************** + * splitter_move + * + * + */ +static void splitter_move(HWND hWnd, LONG delta) +{ + NMHDR nmh; + RECT rect; + + GetClientRect(hWnd, &rect); + MapWindowPoints(hWnd, GetParent(hWnd), (POINT*)&rect, 2); + if (GetWindowLong(hWnd, GWL_STYLE) & SPS_HORIZONTAL) + { + rect.top += delta; + rect.bottom += delta; + } + else + { + rect.left += delta; + rect.right += delta; + } + MoveWindow(hWnd, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, FALSE); + + nmh.hwndFrom = hWnd; + nmh.idFrom = GetDlgCtrlID(hWnd); + nmh.code = NM_CLICK; + SendMessage(GetParent(hWnd), WM_NOTIFY, nmh.idFrom, (LPARAM)&nmh); + + UpdateWindow(hWnd); +} + +/****************************************************************** + * SplitterWndProc + * + * + */ +static LRESULT CALLBACK SplitterWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RECT rect; + POINT pt; + + switch (uMsg) + { + case SPM_GETPOS: + GetClientRect(hWnd, &rect); + MapWindowPoints(hWnd, GetParent(hWnd), (POINT*)&rect, 2); + *((LONG*)lParam) = (GetWindowLong(hWnd, GWL_STYLE) & SPS_HORIZONTAL) ? rect.top : rect.left; + break; + case WM_MOUSEMOVE: + SetCursor(LoadCursor(NULL, (GetWindowLong(hWnd, GWL_STYLE) & SPS_HORIZONTAL) ? IDC_SIZENS : IDC_SIZEWE)); + if ((wParam == MK_LBUTTON) && GetCapture() == hWnd) + { + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + MapWindowPoints(hWnd, GetParent(hWnd), &pt, 1); + splitter_move(hWnd, ((GetWindowLong(hWnd, GWL_STYLE) & SPS_HORIZONTAL) ? pt.y : pt.x) - GetWindowLong(hWnd, 0)); + SetWindowLong(hWnd, 0, (GetWindowLong(hWnd, GWL_STYLE) & SPS_HORIZONTAL) ? pt.y : pt.x); + } + return 0; + case WM_LBUTTONDOWN: + SetCapture(hWnd); + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + MapWindowPoints(hWnd, GetParent(hWnd), &pt, 1); + SetWindowLong(hWnd, 0, (GetWindowLong(hWnd, GWL_STYLE) & SPS_HORIZONTAL) ? pt.y : pt.x); + return 0; + case WM_LBUTTONUP: + ReleaseCapture(); + if ((wParam == MK_LBUTTON) && GetCapture() == hWnd) + { + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + MapWindowPoints(hWnd, GetParent(hWnd), &pt, 1); + splitter_move(hWnd, ((GetWindowLong(hWnd, GWL_STYLE) & SPS_HORIZONTAL) ? pt.y : pt.x) - GetWindowLong(hWnd, 0)); + } + return 0; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +/****************************************************************** + * init_splitter + * + * + */ +BOOL init_splitter(void) +{ + WNDCLASS wndclass; + + wndclass.style = 0; + wndclass.lpfnWndProc = SplitterWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = sizeof(LONG); + wndclass.hInstance = GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon(NULL, IDI_INFORMATION); + wndclass.hCursor = NULL; + wndclass.hbrBackground = (HBRUSH)GetStockObject(/*LTGRAY*/BLACK_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = "MySplitter"; + + return RegisterClass(&wndclass); +} --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/splitter.h 2003-08-30 09:33:31.000000000 +0200 @@ -0,0 +1,9 @@ +BOOL init_splitter(void); + +/* splitter messages */ +#define SPM_GETPOS (WM_USER+0) + +/* splitter window styles */ +#define SPS_VERTICAL 0x0000 +#define SPS_HORIZONTAL 0x0001 +#define SPS_VISUALMOVE 0x0002 --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/system.c 2003-08-31 11:28:27.000000000 +0200 @@ -0,0 +1,347 @@ +#include <stdlib.h> +#include <stdio.h> +#include <psapi.h> +#include <tlhelp32.h> +#include "wineproc.h" + +/****************************************************************** + * open_process + * + * Returns a handle to a running process from its pid, with a given + * set of access rights + */ +HANDLE open_process(DWORD pid, DWORD access) +{ + return (pid) ? OpenProcess(access, FALSE, pid) : NULL; +} + +/****************************************************************** + * get_process_name + * + * + */ +void get_process_name(HANDLE hProcess, char* buffer, unsigned size) +{ + HMODULE hMod[1024]; + DWORD len; + + if (!EnumProcessModules(hProcess, hMod, sizeof(hMod), &len) || + !GetModuleBaseNameA(hProcess, hMod[0], buffer, size)) + snprintf(buffer, size, "--Unknown-- (%lu)", GetLastError()); +} + +/****************************************************************** + * enum_processes + * + * list all known processes in the system + */ +void enum_processes(EnumProcessesCB epcb, DWORD access, void* user) +{ + DWORD pids[1024]; + DWORD cb; + int i, ret = 1; + HANDLE hProcess; + + /* FIXME: this shouldn't fail if buffer is too small, but we + * should grow internal buffer instead + */ + if (!EnumProcesses(pids, sizeof(pids), &cb)) return; + + for (i = cb / sizeof(pids[0]) - 1; i >= 0 && ret; i--) + { + hProcess = open_process(pids[i], access); + ret = epcb(hProcess, pids[i], user); + CloseHandle(hProcess); + } +} + +/****************************************************************** + * enum_modules + * + * + */ +void enum_modules(HANDLE hProcess, EnumModulesCB em, void* user) +{ + HMODULE hMod[1024]; + MODULEINFO mi; + DWORD cb; + int i, ret = 1; + struct module_info smi; + + /* FIXME: if this fails, we should grow the hMod size */ + if (EnumProcessModules(hProcess, hMod, sizeof(hMod), &cb)) + { + for (i = 0; i < cb / sizeof(hMod[0]) && ret; i++) + { + if (!GetModuleBaseNameA(hProcess, hMod[i], smi.module, sizeof(smi.module))) + strcpy(smi.module, "--Unknown--"); + if (!GetModuleFileNameExA(hProcess, hMod[i], smi.file, sizeof(smi.file))) + strcpy(smi.file, "--Unknown--"); + if (!GetModuleInformation(hProcess, hMod[i], &mi, sizeof(mi))) + { + smi.dll_base = NULL; + smi.size = 0; + smi.entry_point = NULL; + } + else + { + smi.dll_base = mi.lpBaseOfDll; + smi.size = mi.SizeOfImage; + smi.entry_point = mi.EntryPoint; + } + ret = em(&smi, user); + } + } +} + +/****************************************************************** + * enum_threads + * + * + */ +void enum_threads(DWORD processID, EnumThreadsCB et, void* user) +{ + HANDLE hSnap, hThread; + THREADENTRY32 te32; + struct thread_info sti; + int ret = 1; + + hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (!hSnap) return; + te32.dwSize = sizeof(te32); + if (Thread32First(hSnap, &te32)) + { + do + { + if (te32.th32OwnerProcessID != processID) continue; + sti.windows_tid = te32.th32ThreadID; + sti.suspend_count = (DWORD)-2; /* error code */ + hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID); + if (hThread != NULL) + { + ULONG status; + if (GetExitCodeThread(hThread, &status)) + { + if (status == STILL_ACTIVE) + { + /* FIXME: this is a bit brutal... + * some nicer way shall be found + */ + if (te32.th32ThreadID != GetCurrentThreadId()) + { + status = SuspendThread(hThread); + if (status != (DWORD)-1) + { + sti.suspend_count = status; + ResumeThread(hThread); + } + } else sti.suspend_count = 0; + } else sti.suspend_count = (DWORD)-1; + } + } + sti.priority = GetThreadPriority(hThread); + ret = et(&sti, user); + CloseHandle(hThread); + } while (ret && Thread32Next(hSnap, &te32)); + } + CloseHandle(hSnap); +} + +#if 1 +/****************************************************************** + * get_symbol + * + * Here it gets ugly :-( + * This is quick hack to get the address of first_dll in a running process + * We make the following assumptions: + * - libwine (lib) is loaded in all processes at the same address (or + * at least at the same address at this process) + * - we load the same libwine.so version in this process and in the + * examined process + * Final address is gotten by: 1/ querying the address of a known exported + * symbol out of libwine.so with dlsym, 2/ then querying nm on libwine.so to + * get the offset from the data segment of this known symbol and of first_dll, + * 3/ computing the actual address of first_dll by adding the result of 1/ and + * the delta of 2/. + * Ugly, yes, but it somehow works. We should replace that with debughlp + * library, that'd be way better. Exporting first_dll from libwine.so would make + * this code simpler, but still ugly. + */ +/* FIXME: we only need those includes for the next function */ +#include <dlfcn.h> /* for RTLD_LAZY */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include "wine/library.h" + +void* get_symbol(HANDLE hProcess, const char* name, const char* lib) +{ + char buffer[1024]; + void* h; + DWORD addr = 0, tmp = 0; + FILE* f; + char* env; + + if (!(h = wine_dlopen(lib, RTLD_LAZY, buffer, sizeof(buffer)))) + { + printf("Couldn't load %s (%s)\n", lib, buffer); + return NULL; + } + + env = getenv("LD_LIBRARY_PATH"); + if (env) + { + char *next, *ptr; + struct stat s; + + for (ptr = env = strdup(env); ptr; ptr = next) + { + next = strchr(ptr, ':'); + if (next) *next++ = '\0'; + sprintf(buffer, "nm %s", ptr); + if (buffer[strlen(buffer) - 1] != '/') strcat(buffer, "/"); + strcat(buffer, lib); + if (stat(buffer + 3, &s) == 0) break; + } + free(env); + if (!ptr) + { + printf("Couldn't find %s in LD_LIBRARY_PATH\n", lib); + return NULL; + } + } + if (!(f = popen(buffer, "r"))) + { + printf("Cannot execute '%s'\n", buffer); + return NULL; + } + + while (fgets(buffer, sizeof(buffer), f)) + { + char *p = buffer + strlen(buffer) - 1; + if (p < buffer) continue; + if (*p == '\n') *p-- = 0; + if (p - buffer < 11) continue; + buffer[8] = '\0'; + if (!strcmp(&buffer[11], name)) addr += strtol(buffer, NULL, 16); + if (buffer[9] == 'D' && !tmp && (tmp = (DWORD)wine_dlsym(h, &buffer[11], NULL, 0)) != 0) + addr += tmp - strtol(buffer, NULL, 16); + } + pclose(f); + return (char*)addr; +} +#else +void* get_symbol(HANDLE hProcess, const char* name, const char* lib) +{ + printf("getSymbol: not implemented on this platform\n"); + return NULL; +} +#endif + +struct dll_option_layout +{ + void* next; + void* prev; + char* const* channels; + int nb_channels; +}; + +/****************************************************************** + * enum_channel + * + * Enumerates all known channels on process hProcess through callback + * ce. + */ +int enum_channel(HANDLE hProcess, EnumChannelCB ce, void* user) +{ + struct dll_option_layout dol; + int i, j, ret = 1; + char* buf_addr; + unsigned char buffer[32]; + void* addr; + const char** cache; + unsigned num_cache, used_cache; + + addr = get_symbol(hProcess, "first_dll", "libwine.so"); + if (!addr) return -1; + cache = malloc((num_cache = 32) * sizeof(char*)); + used_cache = 0; + + while (ret && addr && ReadProcessMemory(hProcess, addr, &dol, sizeof(dol), NULL)) + { + for (i = 0; i < dol.nb_channels; i++) + { + if (ReadProcessMemory(hProcess, (void*)(dol.channels + i), &buf_addr, sizeof(buf_addr), NULL) && + ReadProcessMemory(hProcess, buf_addr, buffer, sizeof(buffer), NULL)) + { + /* since some channels are defined in multiple compilation units, + * they will appear several times... + * so cache the channel's names we already reported and don't report + * them again + */ + for (j = 0; j < used_cache; j++) + if (!strcmp(cache[j], buffer + 1)) break; + + if (j == used_cache) + { + if (used_cache == num_cache) + cache = realloc(cache, (num_cache *= 2) * sizeof(char*)); + cache[used_cache++] = strdup(buffer + 1); + ret = ce(hProcess, buf_addr, buffer, user); + } + } + } + addr = dol.next; + } + for (j = 0; j < used_cache; j++) free((char*)cache[j]); + free(cache); + return 0; +} + +struct cce_user +{ + const char* name; /* channel to look for */ + unsigned value, mask; /* how to change channel */ + unsigned done; /* number of successful changes */ +}; + +/****************************************************************** + * change_channel_CB + * + * Callback used for changing a given channel attributes + */ +static int change_channel_CB(HANDLE hProcess, void* addr, char* buffer, void* pmt) +{ + struct cce_user* user = (struct cce_user*)pmt; + + if (!user->name || !strcmp(buffer + 1, user->name)) + { + buffer[0] = (buffer[0] & ~user->mask) | (user->value & user->mask); + if (WriteProcessMemory(hProcess, addr, buffer, 1, NULL)) + { + user->done++; + } + else printf("Couldn't write back memory (%lu) at %p\n", GetLastError(), addr); + } + return 1; +} + +/****************************************************************** + * change_channel + * + * change state of a debug channel for a given process + */ +int change_channel(HANDLE hProcess, const char* name, unsigned value, unsigned mask) +{ + struct cce_user user; + + user.name = name; + user.value = value; + user.mask = mask; + user.done = 0; + + enum_channel(hProcess, change_channel_CB, &user); + return user.done; +} + --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/visual.c 2003-09-03 21:06:09.000000000 +0200 @@ -0,0 +1,835 @@ +#include <stdio.h> +#include <windows.h> +#include <commctrl.h> +#include "wineproc.h" +#include "wineprocres.h" +#include "splitter.h" + +/* current selected process handled (if any) */ +static DWORD S_CurrPid; + +static struct config +{ + BOOL horzSplit; + int timer; /* interval between updates in tenth of second, -1 means disabled */ +} config; + +/* a couple of UI dimensions definition */ +#define TB_HEIGHT 32 /* height of toolbar */ +#define SPACEX 5 /* horizontal separation of elements */ +#define SPACEY 5 /* vertical separation of elements */ + +#define NUM_OF(x) (sizeof(x) / sizeof(x[0])) + +struct button_tb +{ + unsigned ids; + unsigned message; +}; + +static struct button_tb button_tb[] = +{ + {IDS_TB_KILL_PROCESS, IDM_KILL_PROCESS}, + {IDS_TB_SUSPEND_THREAD, IDM_SUSPEND_THREAD}, + {IDS_TB_RESUME_THREAD, IDM_RESUME_THREAD}, + {IDS_TB_REFRESH, IDM_REFRESH}, + {0, 0}, + {IDS_TB_ABOUT, IDM_ABOUT}, +}; + +struct column_lv +{ + unsigned ids_name; + unsigned width; +}; + +static struct column_lv process_lv[] = +{ + {IDS_PROCESS_PID, 40}, + {IDS_PROCESS_PROCESS, 60} +}; +static struct column_lv module_lv[] = +{ + {IDS_DTL_MODULE_NAME, 20}, + {IDS_DTL_MODULE_FILE, 60}, + {IDS_DTL_MODULE_ADDRESS, 10}, + {IDS_DTL_MODULE_SIZE, 10} +}; +static struct column_lv thread_lv[] = +{ + {IDS_DTL_THREAD_TID, 20}, + {IDS_DTL_THREAD_STATE, 40}, + {IDS_DTL_THREAD_PRIORITY, 40}, +}; +static struct column_lv channel_lv[] = +{ + {IDS_DTL_CHANNEL_NAME, 40}, + {IDS_DTL_CHANNEL_FIXME, 15}, + {IDS_DTL_CHANNEL_ERR, 15}, + {IDS_DTL_CHANNEL_WARN, 15}, + {IDS_DTL_CHANNEL_TRACE, 15} +}; + +static void update_module_listview(HWND hModuleLV); +static void update_thread_listview(HWND hThreadLV); +static void update_channel_listview(HWND hChannelLV); + +typedef struct +{ + struct column_lv* columns; + unsigned num_columns; + unsigned idc; + unsigned ids_name; + void (*update)(HWND); +} details_t; + +static details_t details[] = +{ + {process_lv, NUM_OF(process_lv), IDC_PROCESS_LV, IDS_PROCESS_TAB, NULL}, + {module_lv, NUM_OF(module_lv), IDC_DTL_MODULE_LV, IDS_DTL_MODULE_TAB, update_module_listview}, + {thread_lv, NUM_OF(thread_lv), IDC_DTL_THREAD_LV, IDS_DTL_THREAD_TAB, update_thread_listview}, + {channel_lv, NUM_OF(channel_lv), IDC_DTL_CHANNEL_LV,IDS_DTL_CHANNEL_TAB, update_channel_listview}, +}; + +/* check that all definitions are in sync */ + +#define CONCAT2(a,b) a##b +#define CONCAT(a,b) CONCAT2(a,b) +#define STATIC_CHECK(a) extern char CONCAT(dummy,__LINE__) [1 / ((a) ? 1 : 0)] +STATIC_CHECK(NUM_OF(details) == (IDC_DTL_LAST - IDC_DTL_FIRST + 2)); + +static void change_current_detail(HWND hwndDlg, unsigned idc); + +/****************************************************************** + * init_listview + * + * + */ +static void init_listview(HWND hwndDlg, details_t* d) +{ + HWND hLV = GetDlgItem(hwndDlg, d->idc); + LVCOLUMN lvc; + unsigned i; + char buffer[1024]; + + lvc.mask = LVCF_FMT | LVCF_TEXT; + lvc.fmt = LVCFMT_LEFT; + lvc.pszText = buffer; + + for (i = 0; i < d->num_columns; i++) + { + if (LoadString(GetModuleHandle(NULL), d->columns[i].ids_name, + buffer, sizeof(buffer))) + ListView_InsertColumn(hLV, i, &lvc); + } +} + +/****************************************************************** + * adjust_listview + * + * + */ +static void adjust_listview(HWND hLV, details_t* d, unsigned width) +{ + LVCOLUMN lvc; + unsigned i; + + lvc.mask = LVCF_WIDTH; + + for (i = 0; i < d->num_columns; i++) + { + lvc.cx = MulDiv(d->columns[i].width, width, 100); + ListView_SetColumn(hLV, i, &lvc); + } +} + +/****************************************************************** + * create_toolbar + * + * + */ +static void create_toolbar(HWND hwndDlg) +{ + HWND hToolbar; + TBBUTTON tbb[NUM_OF(button_tb)]; + char buffer[1024]; + HINSTANCE hInst = GetModuleHandle(NULL); + unsigned i; + + hToolbar = CreateWindow(TOOLBARCLASSNAME, NULL, + WS_CHILD | CCS_ADJUSTABLE | TBSTYLE_FLAT, + 0, 0, 1, 1, hwndDlg, (HMENU)IDC_TOOLBAR, + GetModuleHandle(NULL), 0); + + + for (i = 0; i < NUM_OF(button_tb); i++) + { + tbb[i].idCommand = button_tb[i].message; + tbb[i].fsState = TBSTATE_ENABLED; + tbb[i].dwData = 0L; + if (button_tb[i].message) + { + tbb[i].iBitmap = I_IMAGENONE; + tbb[i].fsStyle = TBSTYLE_BUTTON; + LoadString(hInst, button_tb[i].ids, buffer, sizeof(buffer)); + tbb[i].iString = SendMessage(hToolbar, TB_ADDSTRING, 0, (LPARAM)buffer); + } + else + { + tbb[i].iBitmap = 0; + tbb[i].fsStyle = BTNS_SEP; + tbb[i].iString = -1; + } + } + + SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0); + SendMessage(hToolbar, TB_ADDBUTTONS, NUM_OF(tbb), (LPARAM)&tbb); + + ShowWindow(hToolbar, SW_SHOW); +} + +/****************************************************************** + * create_tabs + * + * + */ +static void create_tabs(HWND hwndDlg) +{ + HWND hTabs; + TCITEM tci; + unsigned i; + char buffer[1024]; + + hTabs = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD | WS_VISIBLE, + 0, 0, 1, 1, hwndDlg, (HMENU)IDC_DETAIL_TAB, + GetModuleHandle(NULL), 0); + + tci.mask = TCIF_TEXT; + tci.pszText = buffer; + + for (i = 1; i < NUM_OF(details); i++) + { + if (LoadString(GetModuleHandle(NULL), details[i].ids_name, + buffer, sizeof(buffer))) + TabCtrl_InsertItem(hTabs, i, &tci); + } +} + +struct lp_user +{ + HWND hProcLV; + BOOL found; +}; + +/****************************************************************** + * list_processes_CB + * + * Performs a basic list operation on running processes + */ +static int list_processes_CB(HANDLE hProcess, DWORD pid, void* user) +{ + char spid[16], bufA[MAX_PATH]; + LVITEM lvi; + int index; + struct lp_user* lpu = (struct lp_user*)user; + + get_process_name(hProcess, bufA, sizeof(bufA)); + + memset(&lvi, 0, sizeof(lvi)); + + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.pszText = spid; + sprintf(spid, "%ld", pid); + lvi.lParam = pid; + + if (pid == S_CurrPid) + { + lvi.mask |= LVIF_STATE; + lvi.state = LVIS_SELECTED; + lvi.stateMask = LVIS_SELECTED; + lpu->found = TRUE; + } + index = ListView_InsertItem(lpu->hProcLV, &lvi); + if (index != -1) ListView_SetItemText(lpu->hProcLV, index, 1, bufA); + return 1; +} + +/****************************************************************** + * update_process_listview + * + * + */ +static void update_process_listview(HWND hProcLV) +{ + struct lp_user lpu; + + ListView_DeleteAllItems(hProcLV); + lpu.hProcLV = hProcLV; + lpu.found = FALSE; + enum_processes(list_processes_CB, PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, (void*)&lpu); + /* current process disapeared, likely it has terminated */ + if (S_CurrPid && !lpu.found) S_CurrPid = 0; + change_current_detail(GetParent(hProcLV), 0); +} + +/****************************************************************** + * kill_process + * + * + */ +static void kill_process(HWND hwndDlg) +{ + HANDLE hProcess = open_process(S_CurrPid, PROCESS_TERMINATE); + if (!hProcess) return; /* FIXME message box */ + if (!TerminateProcess(hProcess, 0)) + { + MessageBox(NULL, "Couldn't kill process", "Error", MB_OK); + return; + } + S_CurrPid = 0; + CloseHandle(hProcess); + update_process_listview(GetDlgItem(hwndDlg, IDC_PROCESS_LV)); +} + +/****************************************************************** + * update_module_listviewCB + * + * + */ +static int update_module_listviewCB(struct module_info* smi, void* user) +{ + char buffer[16]; + LVITEM lvi; + int index; + HWND hModuleLV = (HWND)user; + + memset(&lvi, 0, sizeof(lvi)); + + lvi.mask = LVIF_TEXT; + lvi.pszText = smi->module; + if ((index = ListView_InsertItem(hModuleLV, &lvi)) == -1) return 0; + + ListView_SetItemText(hModuleLV, index, 1, smi->file); + + sprintf(buffer, "%p", smi->dll_base); + ListView_SetItemText(hModuleLV, index, 2, buffer); + + sprintf(buffer, "0x%x", smi->size); + ListView_SetItemText(hModuleLV, index, 3, buffer); + + return 1; +} + +/****************************************************************** + * update_module_listview + * + * + */ +static void update_module_listview(HWND hModuleLV) +{ + HANDLE hProcess; + + ListView_DeleteAllItems(hModuleLV); + + hProcess = open_process(S_CurrPid, PROCESS_QUERY_INFORMATION|PROCESS_VM_READ); + if (!hProcess) return; /* FIXME MessageBox */ + enum_modules(hProcess, update_module_listviewCB, (void*)hModuleLV); + CloseHandle(hProcess); +} + +/****************************************************************** + * update_thread_listviewCB + * + * + */ +static int update_thread_listviewCB(struct thread_info* sti, void* user) +{ + char buffer[32]; + LVITEM lvi; + int index; + HWND hThreadLV = (HWND)user; + + memset(&lvi, 0, sizeof(lvi)); + + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.pszText = buffer; + lvi.lParam = sti->windows_tid; + sprintf(buffer, "%u", sti->windows_tid); + if ((index = ListView_InsertItem(hThreadLV, &lvi)) == -1) return 0; + + switch (sti->suspend_count) + { + case -2: + LoadString(GetModuleHandle(NULL), + IDS_DTL_THREAD_STATE_UNKNOWN, + buffer, sizeof(buffer)); + break; + case -1: + LoadString(GetModuleHandle(NULL), + IDS_DTL_THREAD_STATE_TERMINATED, + buffer, sizeof(buffer)); + break; + case 0: + LoadString(GetModuleHandle(NULL), + IDS_DTL_THREAD_STATE_RUNNING, + buffer, sizeof(buffer)); + break; + default: + LoadString(GetModuleHandle(NULL), + IDS_DTL_THREAD_STATE_SUSPENDED, + buffer, sizeof(buffer)); + sprintf(buffer + strlen(buffer), "<%d>", sti->suspend_count); + break; + } + ListView_SetItemText(hThreadLV, index, 1, buffer); + + sprintf(buffer, "%u", sti->priority); + ListView_SetItemText(hThreadLV, index, 2, buffer); + + return 1; +} + +/****************************************************************** + * update_thread_listview + * + * + */ +static void update_thread_listview(HWND hThreadLV) +{ + ListView_DeleteAllItems(hThreadLV); + enum_threads(S_CurrPid, update_thread_listviewCB, (void*)hThreadLV); +} + +/****************************************************************** + * get_selected_thread + * + * + */ +static ULONG get_selected_thread(HWND hThreadLV) +{ + int idx; + LVITEM lvi; + + idx = TabCtrl_GetCurSel(GetDlgItem(GetParent(hThreadLV), IDC_DETAIL_TAB)); + + if (S_CurrPid == 0 || (IDC_DTL_FIRST + idx != IDC_DTL_THREAD_LV) || + (idx = ListView_GetSelectionMark(hThreadLV) == -1)) + { + MessageBox(GetParent(hThreadLV), "No thread selected", "Error", MB_OK); + return (ULONG)-1;; + } + lvi.mask = LVIF_PARAM; + lvi.iItem = idx; + lvi.iSubItem = 0; + if (ListView_GetItem(hThreadLV, &lvi)) return lvi.lParam; + return (ULONG)-1; +} + +/****************************************************************** + * suspend_thread + * + * + */ +static void suspend_thread(HWND hwndDlg) +{ + HWND hThreadLV = GetDlgItem(hwndDlg, IDC_DTL_THREAD_LV); + ULONG tid; + HANDLE hThread; + + tid = get_selected_thread(hThreadLV); + if (tid == (ULONG)-1) return; + + if (tid == GetCurrentThreadId()) + MessageBox(hwndDlg, "Cannot suspend self", "Error", MB_OK); + else + { + hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid); + if (hThread != NULL) + { + if (SuspendThread(hThread) == (DWORD)-1) + MessageBox(hwndDlg, "Cannot suspend thread", "Error", MB_OK); + CloseHandle(hThread); + } + else MessageBox(hwndDlg, "Cannot open thread", "Error", MB_OK); + } + update_thread_listview(hThreadLV); +} + +/****************************************************************** + * resume_thread + * + * + */ +static void resume_thread(HWND hwndDlg) +{ + HWND hThreadLV = GetDlgItem(hwndDlg, IDC_DTL_THREAD_LV); + ULONG tid; + HANDLE hThread; + + tid = get_selected_thread(hThreadLV); + if (tid == (ULONG)-1) return; + + hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid); + if (hThread != NULL) + { + if (ResumeThread(hThread) == (DWORD)-1) + MessageBox(hwndDlg, "Cannot suspend thread", "Error", MB_OK); + CloseHandle(hThread); + } + else MessageBox(hwndDlg, "Cannot open thread", "Error", MB_OK); + update_thread_listview(hThreadLV); +} + +/****************************************************************** + * list_channel_CB + * + * + */ +static int list_channel_CB(HANDLE hProcess, void* addr, char* buffer, void* user) +{ + int j; + char val[2]; + LVITEM lvi; + int index; + HWND hChannelLV = (HWND)user; + + memset(&lvi, 0, sizeof(lvi)); + + lvi.mask = LVIF_TEXT; + lvi.pszText = buffer + 1; + val[1] = '\0'; + + index = ListView_InsertItem(hChannelLV, &lvi); + if (index == -1) return 0; + + for (j = 0; j < 4; j++) + { + val[0] = (buffer[0] & (1 << j)) ? 'x' : ' '; + ListView_SetItemText(hChannelLV, index, j + 1, val); + } + return 1; +} + +/****************************************************************** + * update_channel_listview + * + * + */ +static void update_channel_listview(HWND hChannelLV) +{ + HANDLE hProcess; + + ListView_DeleteAllItems(hChannelLV); + hProcess = open_process(S_CurrPid, PROCESS_VM_OPERATION | PROCESS_VM_READ); + if (!hProcess) return; /* FIXME messagebox */ + enum_channel(hProcess, list_channel_CB, (void*)hChannelLV); + CloseHandle(hProcess); +} + +/****************************************************************** + * interact_listview + * + * + */ +static void interact_listview(HWND hwndDlg, NMITEMACTIVATE* nmia) +{ + LVHITTESTINFO lhti; + HWND hChannelLV; + HANDLE hProcess; + + switch (nmia->hdr.idFrom) + { + case IDC_DTL_CHANNEL_LV: + hProcess = open_process(S_CurrPid, PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE); + if (!hProcess) return; /* FIXME message box */ + lhti.pt = nmia->ptAction; + hChannelLV = GetDlgItem(hwndDlg, IDC_DTL_CHANNEL_LV); + SendMessage(hChannelLV, LVM_SUBITEMHITTEST, 0, (LPARAM)&lhti); + if (nmia->iSubItem >= 1 && nmia->iSubItem <= 4) + { + char val[2]; + char name[32]; + unsigned bitmask = 1 << (lhti.iSubItem - 1); + + ListView_GetItemText(hChannelLV, lhti.iItem, 0, name, sizeof(name)); + ListView_GetItemText(hChannelLV, lhti.iItem, lhti.iSubItem, val, sizeof(val)); + if (change_channel(hProcess, name, val[0] == 'x' ? 0 : bitmask, bitmask)) + { + val[0] ^= ('x' ^ ' '); + ListView_SetItemText(hChannelLV, lhti.iItem, lhti.iSubItem, val); + } + } + CloseHandle(hProcess); + break; + } +} + +/****************************************************************** + * change_current_detail + * + * + */ +static void change_current_detail(HWND hwndDlg, unsigned idc_new) +{ + static unsigned curr_tab; + + unsigned idc; + + if (idc_new) + { + if (curr_tab == idc_new) return; + curr_tab = idc_new; + } + for (idc = IDC_DTL_FIRST; idc <= IDC_DTL_LAST; idc++) + ShowWindow(GetDlgItem(hwndDlg, idc), (idc == curr_tab) ? SW_SHOW : SW_HIDE); + + if (curr_tab >= IDC_DTL_FIRST && curr_tab <= IDC_DTL_LAST) + { + HWND hLV = GetDlgItem(hwndDlg, curr_tab); + + SendMessage(hLV, WM_SETREDRAW, FALSE, 0); + details[curr_tab - IDC_DTL_FIRST + 1].update(hLV); + SendMessage(hLV, WM_SETREDRAW, TRUE, 0); + } +} + +/****************************************************************** + * change_current_process + * + * + */ +static void change_current_process(HWND hwndDlg, NMITEMACTIVATE* nmia) +{ + HWND hProcLV = GetDlgItem(hwndDlg, IDC_PROCESS_LV); + LVITEM lvi; + + if (nmia->iItem == -1) return; + + lvi.mask = LVIF_PARAM; + lvi.iItem = nmia->iItem; + lvi.iSubItem = 0; + if (ListView_GetItem(hProcLV, &lvi)) + { + S_CurrPid = lvi.lParam; + change_current_detail(hwndDlg, 0); + } +} + +/****************************************************************** + * recompute_position + * + * + */ +static void recompute_position(HWND hwndDlg, BOOL init_split, BOOL repaint) +{ + HWND hTabs = GetDlgItem(hwndDlg, IDC_DETAIL_TAB); + RECT r; + LONG w, h, split; + int idc; + + /* adjust two main windows */ + GetClientRect(hwndDlg, &r); + + if (config.horzSplit) + { + w = r.right - r.left - 2 * SPACEX; + h = r.bottom - r.top - TB_HEIGHT - 4 * SPACEY; + + if (init_split) + { + split = h / 2; + MoveWindow(GetDlgItem(hwndDlg, IDC_SPLITTER), SPACEX, TB_HEIGHT + 2 * SPACEY + split, w, SPACEY, repaint); + } + else + { + SendDlgItemMessage(hwndDlg, IDC_SPLITTER, SPM_GETPOS, 0L, (LPARAM)&split); + split -= TB_HEIGHT + 2 * SPACEY; + /* may also have to change splitter width (on size modification) */ + } + + MoveWindow(GetDlgItem(hwndDlg, IDC_PROCESS_LV), SPACEX, TB_HEIGHT + 2 * SPACEY, w, split, repaint); + MoveWindow(hTabs, SPACEX, TB_HEIGHT + 3 * SPACEY + split, w, h - split, repaint); + adjust_listview(GetDlgItem(hwndDlg, IDC_PROCESS_LV), &details[0], w); + + /* adjust second listview in tab control */ + GetClientRect(hTabs, &r); + TabCtrl_AdjustRect(hTabs, FALSE, &r); + for (idc = IDC_DTL_FIRST; idc <= IDC_DTL_LAST; idc++) + { + HWND hLV = GetDlgItem(hwndDlg, idc); + MoveWindow(hLV, SPACEX, TB_HEIGHT + 3 * SPACEY + split + r.top, + r.right - r.left, r.bottom - r.top, repaint); + adjust_listview(hLV, &details[idc - IDC_DTL_FIRST + 1], w); + } + } + else + { + w = r.right - r.left - 4 * SPACEX; + h = r.bottom - r.top - TB_HEIGHT - 2 * SPACEY; + + if (init_split) + { + split = w / 2; + MoveWindow(GetDlgItem(hwndDlg, IDC_SPLITTER), SPACEX + split, TB_HEIGHT + 2 * SPACEY, SPACEX, h, repaint); + } + else + { + SendDlgItemMessage(hwndDlg, IDC_SPLITTER, SPM_GETPOS, 0L, (LPARAM)&split); + split -= SPACEX; + /* may also have to change splitter height (on size modification) */ + } + + MoveWindow(GetDlgItem(hwndDlg, IDC_PROCESS_LV), SPACEX, TB_HEIGHT + 2 * SPACEY, split, h, repaint); + MoveWindow(hTabs, 2 * SPACEX + split, TB_HEIGHT + 2 * SPACEY, w - split, h, repaint); + adjust_listview(GetDlgItem(hwndDlg, IDC_PROCESS_LV), &details[0], split); + + /* adjust second listview in tab control */ + GetClientRect(hTabs, &r); + TabCtrl_AdjustRect(hTabs, FALSE, &r); + for (idc = IDC_DTL_FIRST; idc <= IDC_DTL_LAST; idc++) + { + HWND hLV = GetDlgItem(hwndDlg, idc); + MoveWindow(hLV, 2 * SPACEX + split + r.left, TB_HEIGHT + 2 * SPACEY + r.top, + r.right - r.left, r.bottom - r.top, repaint); + adjust_listview(hLV, &details[idc - IDC_DTL_FIRST + 1], w - split); + } + } + MoveWindow(GetDlgItem(hwndDlg, IDC_TOOLBAR), SPACEX, SPACEY, w, TB_HEIGHT, repaint); +} + +/****************************************************************** + * init_dlg + * + * + */ +static void init_dlg(HWND hwndDlg) +{ + unsigned i; + + InitCommonControls(); + init_splitter(); + + for (i = 0; i < NUM_OF(details); i++) + { + /* LVS_SINGLESEL */ + CreateWindow(WC_LISTVIEW, NULL, + WS_CHILD | LVS_REPORT | LVS_SHOWSELALWAYS | /*LVS_SORTASCENDING | */WS_VISIBLE, + 0, 0, 1, 1, hwndDlg, (HMENU)details[i].idc, GetModuleHandle(NULL), 0); + init_listview(hwndDlg, &details[i]); + } + create_toolbar(hwndDlg); + create_tabs(hwndDlg); + CreateWindow("MySplitter", NULL, WS_CHILD | WS_VISIBLE | (config.horzSplit ? SPS_HORIZONTAL : SPS_VERTICAL), + 0, 0, 1, 1, hwndDlg, (HMENU)IDC_SPLITTER, GetModuleHandle(NULL), 0); + + recompute_position(hwndDlg, TRUE, FALSE); + if (config.timer != -1) SetTimer(hwndDlg, 0, config.timer * 100, NULL); + ListView_SetExtendedListViewStyle(GetDlgItem(hwndDlg, IDC_PROCESS_LV), + LVS_EX_FULLROWSELECT); + ListView_SetExtendedListViewStyle(GetDlgItem(hwndDlg, IDC_DTL_THREAD_LV), + LVS_EX_FULLROWSELECT); + + update_process_listview(GetDlgItem(hwndDlg, IDC_PROCESS_LV)); +} + +/****************************************************************** + * DlgProc + * + * + */ +static BOOL CALLBACK DlgProc(HWND hwndDlg, UINT message, + WPARAM wParam, LPARAM lParam) +{ + NMHDR* nmh; + + switch (message) + { + case WM_INITDIALOG: + init_dlg(hwndDlg); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + case IDCANCEL: + KillTimer(hwndDlg, 0); + EndDialog(hwndDlg, wParam); + break; + case IDM_KILL_PROCESS: + kill_process(hwndDlg); + break; + case IDM_SUSPEND_THREAD: + suspend_thread(hwndDlg); + break; + case IDM_RESUME_THREAD: + resume_thread(hwndDlg); + break; + case IDM_ABOUT: + MessageBox(hwndDlg, "NIY", "NIY", MB_OK); + break; + case IDM_REFRESH: + update_process_listview(GetDlgItem(hwndDlg, IDC_PROCESS_LV)); + break; + default: + printf("Got command %u\n", LOWORD(wParam)); + break; + } + break; + case WM_NOTIFY: + nmh = (NMHDR*)lParam; + switch (nmh->code) + { + case NM_CLICK: + switch (nmh->idFrom) + { + case IDC_PROCESS_LV: + change_current_process(hwndDlg, (NMITEMACTIVATE*)lParam); + break; + case IDC_DETAIL_TAB: + change_current_detail(hwndDlg, IDC_DTL_FIRST + TabCtrl_GetCurSel(nmh->hwndFrom)); + break; + case IDC_SPLITTER: + recompute_position(hwndDlg, FALSE, TRUE); + break; + } + if (nmh->idFrom >= IDC_DTL_FIRST && nmh->idFrom <= IDC_DTL_LAST) + interact_listview(hwndDlg, (NMITEMACTIVATE*)lParam); + break; + } + break; + case WM_SIZE: + recompute_position(hwndDlg, FALSE, TRUE); + return TRUE; + case WM_TIMER: + update_process_listview(GetDlgItem(hwndDlg, IDC_PROCESS_LV)); + return TRUE; + } + + return FALSE; +} + +/****************************************************************** + * load_configuration + * + * + */ +static void load_configuration(void) +{ + /* FIXME: should be moved to some configuration in registry */ + config.horzSplit = TRUE; + config.timer = 30; +} + +/****************************************************************** + * visual + * + * + */ +int visual(void) +{ + load_configuration(); + DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_MAIN_DIALOG), + NULL, DlgProc); + return 0; +} + --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/wineproc.c 2003-09-01 22:13:12.000000000 +0200 @@ -0,0 +1,404 @@ +/* + * WineProc + * + * a command-line tool for wine process management + * + * (c) Eric Pouech 2003 + * + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include "wineproc.h" + +static int usage(void) +{ + printf("WineProc (v 0.01), a command-line tool for wine process management\n\n" + "Usage:\n" + " wineproc debugmsg <pid> <c> change debug channel on process <pid>\n" + " <c> has the same syntax as on the -debugmsg option\n" + " wineproc module <pid> prints modules' information on process <pid>\n" + " wineproc thread <pid> prints thread' information on process <pid>\n" + " wineproc kill <pid> kills process <pid>\n" + " wineproc list prints a list of running processes\n" + "\n" + " <pid> is a unique identifier for a process. It can either be its process id \n" + " (in hex or decimal), or the name of the process\n" + "\n" + " wineproc launches the visual version of wineproc\n" + ); + return -1; +} + +/****************************************************************** + * fatal + * + * Print out a fatal error message and exit + */ +static void fatal(const char *msg, ...) +{ + va_list valist; + va_start(valist, msg); + vfprintf(stderr, msg, valist); + va_end(valist); + exit(-1); +} + +struct openProcessStrUser +{ + const char* str_pid; + HANDLE hProcess; + DWORD pid; +}; + +/****************************************************************** + * open_process_from_string_CB + * + * + */ +static int open_process_from_string_CB(HANDLE hProcess, DWORD pid, void* user) +{ + struct openProcessStrUser* opsu = (struct openProcessStrUser*)user; + char buffer[MAX_PATH]; + char* ptr; + + get_process_name(hProcess, buffer, sizeof(buffer)); + ptr = strrchr(buffer, '\\'); + if (!ptr) ptr = buffer; + if (strcasecmp(opsu->str_pid, ptr) != 0) + { + char* ptr2 = strrchr(ptr, '.'); + if (!ptr2) return 1; + *ptr2 = '\0'; + if (strcasecmp(opsu->str_pid, ptr) != 0) return 1; + } + if (opsu->hProcess) return 1; /* FIXME need error handling here */ + opsu->pid = pid; + DuplicateHandle(GetCurrentProcess(), hProcess, + GetCurrentProcess(), &opsu->hProcess, + 0, FALSE, DUPLICATE_SAME_ACCESS); + return 1; +} + +/****************************************************************** + * open_process_from_string + * + * Returns a handle to a running process from its pid (in string form), + * with a given set of access rights + */ +static HANDLE open_process_from_string(const char* str_pid, DWORD access, DWORD* ppid) +{ + char* end; + DWORD pid = strtol(str_pid, &end, 0); + HANDLE hProcess; + + if (*end != '\0') + { + struct openProcessStrUser opsu; + + opsu.str_pid = str_pid; + opsu.hProcess = NULL; + enum_processes(open_process_from_string_CB, access, &opsu); + hProcess = opsu.hProcess; + if (ppid) *ppid = opsu.pid; + } + else + { + hProcess = open_process(pid, access); + if (ppid) *ppid = pid; + } + if (!hProcess) fatal("Couldn't open process %s (%lu)\n", str_pid, GetLastError()); + return hProcess; +} + +/****************************************************************** + * open_thread_from_string + * + * + */ +static HANDLE open_thread_from_string(const char* str_tid, DWORD access) +{ + char* end; + DWORD tid = strtol(str_tid, &end, 0); + HANDLE hThread; + + if (*end != '\0') fatal("Malformed tid %s\n", str_tid); + hThread = OpenThread(access, FALSE, tid); + if (!hThread) fatal("Couldn't open thread %s (%lu)\n", str_tid, GetLastError()); + return hThread; +} + +/****************************************************************** + * list_processes_CB + * + * Performs a basic list operation on running processes + */ +static int list_processes_CB(HANDLE hProcess, DWORD pid, void* user) +{ + char bufA[MAX_PATH]; + + get_process_name(hProcess, bufA, sizeof(bufA)); + printf("%-8lx %s%s\n", pid, bufA, GetCurrentProcessId() == pid ? " <-- self" : ""); + return 1; +} + +/****************************************************************** + * list_processes + * + * + */ +static int list_processes(void) +{ + printf("Pid Executable\n"); + enum_processes(list_processes_CB, PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, NULL); + return 0; +} + +/****************************************************************** + * print_process_moduleCB + * + * + */ +static int print_process_moduleCB(struct module_info* smi, void* user) +{ + printf("%-12s %-8lx %-8x %8p %s\n", + smi->module, (DWORD)smi->dll_base, smi->size, smi->entry_point, smi->file); + return 1; +} + +/****************************************************************** + * print_process_module + * + * List loaded modules from a given module + */ +static int print_process_module(const char* str_pid) +{ + HANDLE hProcess; + + hProcess = open_process_from_string(str_pid, PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, NULL); + printf("Module Base Size EntryPoint File\n"); + enum_modules(hProcess, print_process_moduleCB, NULL); + CloseHandle(hProcess); + return 0; +} + +/****************************************************************** + * print_process_threadCB + * + * + */ +static int print_process_threadCB(struct thread_info* sti, void* user) +{ + char buffer[32]; + + switch (sti->suspend_count) + { + case -2: strcpy(buffer, "--Unknown--"); break; + case -1: strcpy(buffer, "Terminated"); break; + case 0: strcpy(buffer, "Running"); break; + default: sprintf(buffer, "Suspended<%d>", sti->suspend_count); break; + } + + printf("%-10u %-8u %-15s\n", sti->windows_tid, sti->priority, buffer); + return 1; +} + +/****************************************************************** + * print_process_thread + * + * List loaded threads from a given thread + */ +static int print_process_thread(const char* str_pid) +{ + HANDLE hProcess; + DWORD pid; + + hProcess = open_process_from_string(str_pid, PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, &pid); + printf("Win-TID Priority State (%lu)\n", pid); + enum_threads(pid, print_process_threadCB, NULL); + CloseHandle(hProcess); + return 0; +} + +/****************************************************************** + * kill_process + * + * Kill a running process + */ +static int kill_process(const char* str_pid) +{ + HANDLE hProcess = open_process_from_string(str_pid, PROCESS_TERMINATE, NULL); + + if (!TerminateProcess(hProcess, 0)) + fatal("Couldn't kill process (%lu)\n", GetLastError()); + CloseHandle(hProcess); + return 0; +} + +/****************************************************************** + * suspend_thread + * + * + */ +static int suspend_thread(const char* str_tid) +{ + HANDLE hThread = open_thread_from_string(str_tid, THREAD_ALL_ACCESS); + if (!hThread) return 0; + if (SuspendThread(hThread) == (ULONG)-1) + fatal("Couldn't suspend %s (%lu)\n", str_tid, GetLastError()); + CloseHandle(hThread); + return 0; +} + +/****************************************************************** + * Resume_thread + * + * + */ +static int resume_thread(const char* str_tid) +{ + HANDLE hThread = open_thread_from_string(str_tid, THREAD_ALL_ACCESS); + if (!hThread) return -1; + if (ResumeThread(hThread) == (ULONG)-1) + fatal("Couldn't resume %s (%lu)\n", str_tid, GetLastError()); + CloseHandle(hThread); + return 0; +} + +static const char * const debug_classes[] = { "fixme", "err", "warn", "trace" }; + +/****************************************************************** + * list_channel_CB + * + * Callback used when listing known channels + */ +static int list_channel_CB(HANDLE hProcess, void* addr, char* buffer, void* pmt) +{ + char tmp[64]; + unsigned len = 0; + int j; + + for (j = 0; j < sizeof(debug_classes)/sizeof(debug_classes[0]); j++) + { + if (buffer[0] & (1 << j)) + { + if (len) tmp[len++] = ','; + strcpy(&tmp[len], debug_classes[j]); + len += strlen(debug_classes[j]); + } + } + tmp[len] = '\0'; + printf("Channel %s: %s\n", buffer + 1, tmp); + return 1; +} + +/****************************************************************** + * list_channel + * + * List all known channels on process of pid str_pid + */ +static int list_channel(const char* str_pid) +{ + HANDLE hProcess = open_process_from_string(str_pid, PROCESS_VM_OPERATION | PROCESS_VM_READ, NULL); + int ret; + + ret = enum_channel(hProcess, list_channel_CB, NULL); + CloseHandle(hProcess); + return ret; +} + +/****************************************************************** + * set_channel + * + * Set new values on channels. + */ +static int set_channel(const char* str_pid, const char* str) +{ + char *opt, *next, *options, *name; + int i; + HANDLE hProcess = open_process_from_string(str_pid, PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, NULL); + unsigned errors = 0, value, mask; + + if (!(options = strdup(str))) return -1; + for (opt = options; opt; opt = next) + { + value = mask = 0; + if ((next = strchr(opt, ','))) *next++ = 0; + + name = opt + strcspn(opt, "+-"); + if (!name[0] || !name[1]) /* bad option, skip it */ + { + errors++; + continue; + } + + if (name > opt) + { + for (i = 0; i < sizeof(debug_classes)/sizeof(debug_classes[0]); i++) + { + int len = strlen(debug_classes[i]); + if (len != (name - opt)) continue; + if (!memcmp(opt, debug_classes[i], len)) /* found it */ + { + if (*name == '+') value |= 1 << i; + mask |= 1 << i; + break; + } + } + if (i == sizeof(debug_classes)/sizeof(debug_classes[0])) /* bad class name, skip it */ + { + errors++; + continue; + } + } + else + { + if (*name == '+') value = 15; + mask = 15; + } + name++; + if (!strcmp(name, "all")) name = NULL; + if (!change_channel(hProcess, name, value, mask)) + { + printf("Unable to find debug channel '%s'\n", name); + errors++; + } + /* FIXME: used to be list_channel_CB(hProcess, addr, buffer, NULL); + * but we need to print back at the user the new state of the changed channel + */ + } + free(options); + CloseHandle(hProcess); + return errors; +} + +/****************************************************************** + * main + * + */ +int main(int argc, char* argv[]) +{ + switch (argc) + { + case 1: + return visual(); + case 2: + if (!strcmp(argv[1], "list")) return list_processes(); + break; + case 3: + if (!strcmp(argv[1], "debugmsg")) return list_channel(argv[2]); + if (!strcmp(argv[1], "module")) return print_process_module(argv[2]); + if (!strcmp(argv[1], "thread")) return print_process_thread(argv[2]); + if (!strcmp(argv[1], "kill")) return kill_process(argv[2]); + if (!strcmp(argv[1], "suspend")) return suspend_thread(argv[2]); + if (!strcmp(argv[1], "resume")) return resume_thread(argv[2]); + break; + case 4: + if (!strcmp(argv[1], "debugmsg")) return set_channel(argv[2], argv[3]); + break; + } + return usage(); +} + --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/wineproc_En.rc 2003-09-01 18:43:49.000000000 +0200 @@ -0,0 +1,37 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_PROCESS_TAB, "Processes" + IDS_PROCESS_PID, "PID" + IDS_PROCESS_PROCESS, "Process" + + IDS_DTL_MODULE_TAB, "Modules" + IDS_DTL_MODULE_NAME, "Name" + IDS_DTL_MODULE_FILE, "File" + IDS_DTL_MODULE_ADDRESS, "Base address" + IDS_DTL_MODULE_SIZE, "Size" + + IDS_DTL_THREAD_TAB, "Threads" + IDS_DTL_THREAD_TID, "TID" + IDS_DTL_THREAD_PRIORITY, "Priority" + IDS_DTL_THREAD_STATE, "State" + + IDS_DTL_THREAD_STATE_RUNNING, "Running" + IDS_DTL_THREAD_STATE_TERMINATED,"Terminated" + IDS_DTL_THREAD_STATE_SUSPENDED, "Suspended" + IDS_DTL_THREAD_STATE_UNKNOWN, "Unknown" + + IDS_DTL_CHANNEL_TAB, "Debug channels" + IDS_DTL_CHANNEL_NAME, "Name" + IDS_DTL_CHANNEL_FIXME, "Fixme" + IDS_DTL_CHANNEL_ERR, "Err" + IDS_DTL_CHANNEL_WARN, "Warn" + IDS_DTL_CHANNEL_TRACE, "Trace" + + IDS_TB_KILL_PROCESS "Kill" + IDS_TB_SUSPEND_THREAD "Suspend" + IDS_TB_RESUME_THREAD "Resume" + IDS_TB_REFRESH "Refresh" + IDS_TB_ABOUT "About" +END --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/wineproc.h 2003-09-01 21:19:08.000000000 +0200 @@ -0,0 +1,39 @@ +#include "windows.h" +#include "winbase.h" + +HANDLE open_process(DWORD pid, DWORD access); + +void get_process_name(HANDLE hProcess, char* buffer, unsigned size); + +typedef int (*EnumProcessesCB)(HANDLE, DWORD, void*); +void enum_processes(EnumProcessesCB epcb, DWORD access, void* user); + +struct module_info +{ + char module[MAX_PATH]; + char file[MAX_PATH]; + void* dll_base; + unsigned size; + void* entry_point; +}; +typedef int (*EnumModulesCB)(struct module_info* smi, void* user); +void enum_modules(HANDLE hProcess, EnumModulesCB em, void* user); + +struct thread_info +{ + unsigned windows_tid; + unsigned suspend_count; /* 0 == running, > 0 suspended, -1 terminated, -2 don't know */ + unsigned priority; +}; +typedef int (*EnumThreadsCB)(struct thread_info* sti, void* user); +void enum_threads(DWORD pid, EnumThreadsCB et, void* user); + +void* get_symbol(HANDLE hProcess, const char* name, const char* lib); + +/* channel enumeration call back prototype */ +typedef int (*EnumChannelCB)(HANDLE, void*, char*, void*); +int enum_channel(HANDLE hProcess, EnumChannelCB ce, void* user); +int change_channel(HANDLE hProcess, const char* name, unsigned value, unsigned mask); + +int visual(void); + --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/wineproc.rc 2003-08-23 18:10:44.000000000 +0200 @@ -0,0 +1,18 @@ +#include "wineprocres.h" +#include <windef.h> +#include <winuser.h> +#include <winnls.h> +#include <commctrl.h> + +IDD_MAIN_DIALOG DIALOG DISCARDABLE 0, 0, 500, 400 +STYLE WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Wine Process" +FONT 8, "MS Sans Serif" +BEGIN +END + +#include "wineproc_En.rc" + + + + --- /dev/null 1970-01-01 01:00:00.000000000 +0100 +++ programs/wineproc/wineprocres.h 2003-09-01 18:44:24.000000000 +0200 @@ -0,0 +1,55 @@ +/* ID for dialogs */ +#define IDD_MAIN_DIALOG 0x1000 +#define IDD_ABOUT 0x1001 + +/* ID for controls */ +#define IDC_PROCESS_LV 0x2000 +#define IDC_DETAIL_TAB 0x2001 +#define IDC_TOOLBAR 0x2002 +#define IDC_SPLITTER 0x2003 + +#define IDC_DTL_FIRST 0x2100 +#define IDC_DTL_MODULE_LV 0x2100 +#define IDC_DTL_THREAD_LV 0x2101 +#define IDC_DTL_CHANNEL_LV 0x2102 +#define IDC_DTL_LAST 0x2102 + +/* ID for string */ +#define IDS_PROCESS_TAB 0x3000 +#define IDS_PROCESS_PID 0x3001 +#define IDS_PROCESS_PROCESS 0x3002 + +#define IDS_DTL_MODULE_TAB 0x3010 +#define IDS_DTL_MODULE_NAME 0x3011 +#define IDS_DTL_MODULE_FILE 0x3012 +#define IDS_DTL_MODULE_ADDRESS 0x3013 +#define IDS_DTL_MODULE_SIZE 0x3014 + +#define IDS_DTL_THREAD_TAB 0x3020 +#define IDS_DTL_THREAD_TID 0x3021 +#define IDS_DTL_THREAD_PRIORITY 0x3022 +#define IDS_DTL_THREAD_STATE 0x3023 +#define IDS_DTL_THREAD_STATE_RUNNING 0x3024 +#define IDS_DTL_THREAD_STATE_TERMINATED 0x3025 +#define IDS_DTL_THREAD_STATE_SUSPENDED 0x3026 +#define IDS_DTL_THREAD_STATE_UNKNOWN 0x3027 + +#define IDS_DTL_CHANNEL_TAB 0x3030 +#define IDS_DTL_CHANNEL_NAME 0x3031 +#define IDS_DTL_CHANNEL_FIXME 0x3032 +#define IDS_DTL_CHANNEL_ERR 0x3033 +#define IDS_DTL_CHANNEL_WARN 0x3034 +#define IDS_DTL_CHANNEL_TRACE 0x3035 + +#define IDS_TB_KILL_PROCESS 0x3100 +#define IDS_TB_SUSPEND_THREAD 0x3101 +#define IDS_TB_RESUME_THREAD 0x3102 +#define IDS_TB_REFRESH 0x3103 +#define IDS_TB_ABOUT 0x3104 + +/* ID for command message */ +#define IDM_KILL_PROCESS 0x4000 +#define IDM_SUSPEND_THREAD 0x4001 +#define IDM_RESUME_THREAD 0x4002 +#define IDM_REFRESH 0x4003 +#define IDM_ABOUT 0x4004