OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <windows.h> | |
6 #include <CommCtrl.h> | |
7 #include <commdlg.h> | |
8 #include <time.h> | |
9 #include <windowsx.h> | |
10 #include <atlbase.h> | |
11 #include <atlsecurity.h> | |
12 #include <algorithm> | |
13 #include <sstream> | |
14 | |
15 #include "sandbox/sandbox_poc/main_ui_window.h" | |
16 #include "base/logging.h" | |
17 #include "sandbox/sandbox_poc/resource.h" | |
18 #include "sandbox/src/acl.h" | |
19 #include "sandbox/src/sandbox.h" | |
20 #include "sandbox/src/win_utils.h" | |
21 | |
22 HWND MainUIWindow::list_view_ = NULL; | |
23 | |
24 const wchar_t MainUIWindow::kDefaultDll_[] = L"\\POCDLL.dll"; | |
25 const wchar_t MainUIWindow::kDefaultEntryPoint_[] = L"Run"; | |
26 const wchar_t MainUIWindow::kDefaultLogFile_[] = L""; | |
27 | |
28 MainUIWindow::MainUIWindow() | |
29 : instance_handle_(NULL), | |
30 spawn_target_(L""), | |
31 dll_path_(L""), | |
32 entry_point_(L""), | |
33 broker_(NULL) { | |
34 } | |
35 | |
36 MainUIWindow::~MainUIWindow() { | |
37 } | |
38 | |
39 unsigned int MainUIWindow::CreateMainWindowAndLoop( | |
40 HINSTANCE instance, | |
41 wchar_t* command_line, | |
42 int show_command, | |
43 sandbox::BrokerServices* broker) { | |
44 DCHECK(instance); | |
45 DCHECK(command_line); | |
46 DCHECK(broker); | |
47 | |
48 instance_handle_ = instance; | |
49 spawn_target_ = command_line; | |
50 broker_ = broker; | |
51 | |
52 // We'll use spawn_target_ later for creating a child process, but | |
53 // CreateProcess doesn't like double quotes, so we remove them along with | |
54 // tabs and spaces from the start and end of the string | |
55 const wchar_t *trim_removal = L" \r\t\""; | |
56 spawn_target_.erase(0, spawn_target_.find_first_not_of(trim_removal)); | |
57 spawn_target_.erase(spawn_target_.find_last_not_of(trim_removal) + 1); | |
58 | |
59 WNDCLASSEX window_class = {0}; | |
60 window_class.cbSize = sizeof(WNDCLASSEX); | |
61 window_class.style = CS_HREDRAW | CS_VREDRAW; | |
62 window_class.lpfnWndProc = MainUIWindow::WndProc; | |
63 window_class.cbClsExtra = 0; | |
64 window_class.cbWndExtra = 0; | |
65 window_class.hInstance = instance; | |
66 window_class.hIcon = | |
67 ::LoadIcon(instance, MAKEINTRESOURCE(IDI_SANDBOX)); | |
68 window_class.hCursor = ::LoadCursor(NULL, IDC_ARROW); | |
69 window_class.hbrBackground = GetStockBrush(WHITE_BRUSH); | |
70 window_class.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_MAIN_UI); | |
71 window_class.lpszClassName = L"sandbox_ui_1"; | |
72 window_class.hIconSm = NULL; | |
73 | |
74 INITCOMMONCONTROLSEX controls = { | |
75 sizeof(INITCOMMONCONTROLSEX), | |
76 ICC_STANDARD_CLASSES | ICC_LISTVIEW_CLASSES | |
77 }; | |
78 ::InitCommonControlsEx(&controls); | |
79 | |
80 if (!::RegisterClassEx(&window_class)) | |
81 return ::GetLastError(); | |
82 | |
83 // Create a main window of size 600x400 | |
84 HWND window = ::CreateWindowW(window_class.lpszClassName, | |
85 L"", // window name | |
86 WS_OVERLAPPEDWINDOW, | |
87 CW_USEDEFAULT, // x | |
88 CW_USEDEFAULT, // y | |
89 600, // width | |
90 400, // height | |
91 NULL, // parent | |
92 NULL, // NULL = use class menu | |
93 instance, | |
94 0); // lpParam | |
95 | |
96 if (NULL == window) | |
97 return ::GetLastError(); | |
98 | |
99 ::SetWindowLongPtr(window, | |
100 GWLP_USERDATA, | |
101 reinterpret_cast<LONG_PTR>(this)); | |
102 | |
103 ::SetWindowText(window, L"Sandbox Proof of Concept"); | |
104 | |
105 ::ShowWindow(window, show_command); | |
106 | |
107 MSG message; | |
108 // Now lets start the message pump retrieving messages for any window that | |
109 // belongs to the current thread | |
110 while (::GetMessage(&message, NULL, 0, 0)) { | |
111 ::TranslateMessage(&message); | |
112 ::DispatchMessage(&message); | |
113 } | |
114 | |
115 return 0; | |
116 } | |
117 | |
118 LRESULT CALLBACK MainUIWindow::WndProc(HWND window, | |
119 UINT message_id, | |
120 WPARAM wparam, | |
121 LPARAM lparam) { | |
122 MainUIWindow* host = FromWindow(window); | |
123 | |
124 #define HANDLE_MSG(hwnd, message, fn) \ | |
125 case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn)) | |
126 | |
127 switch (message_id) { | |
128 case WM_CREATE: | |
129 // 'host' is not yet available when we get the WM_CREATE message | |
130 return HANDLE_WM_CREATE(window, wparam, lparam, OnCreate); | |
131 case WM_DESTROY: | |
132 return HANDLE_WM_DESTROY(window, wparam, lparam, host->OnDestroy); | |
133 case WM_SIZE: | |
134 return HANDLE_WM_SIZE(window, wparam, lparam, host->OnSize); | |
135 case WM_COMMAND: { | |
136 // Look at which menu item was clicked on (or which accelerator) | |
137 int id = LOWORD(wparam); | |
138 switch (id) { | |
139 case ID_FILE_EXIT: | |
140 host->OnFileExit(); | |
141 break; | |
142 case ID_COMMANDS_SPAWNTARGET: | |
143 host->OnCommandsLaunch(window); | |
144 break; | |
145 default: | |
146 // Some other menu item or accelerator | |
147 break; | |
148 } | |
149 | |
150 return ERROR_SUCCESS; | |
151 } | |
152 | |
153 default: | |
154 // Some other WM_message, let it pass to DefWndProc | |
155 break; | |
156 } | |
157 | |
158 return DefWindowProc(window, message_id, wparam, lparam); | |
159 } | |
160 | |
161 INT_PTR CALLBACK MainUIWindow::SpawnTargetWndProc(HWND dialog, | |
162 UINT message_id, | |
163 WPARAM wparam, | |
164 LPARAM lparam) { | |
165 UNREFERENCED_PARAMETER(lparam); | |
166 | |
167 // Grab a reference to the main UI window (from the window handle) | |
168 MainUIWindow* host = FromWindow(GetParent(dialog)); | |
169 DCHECK(host); | |
170 | |
171 switch (message_id) { | |
172 case WM_INITDIALOG: { | |
173 // Initialize the window text for DLL name edit box | |
174 HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME); | |
175 wchar_t current_dir[MAX_PATH]; | |
176 if (GetCurrentDirectory(MAX_PATH, current_dir)) { | |
177 std::wstring dll_path = std::wstring(current_dir) + | |
178 std::wstring(kDefaultDll_); | |
179 ::SetWindowText(edit_box_dll_name, dll_path.c_str()); | |
180 } | |
181 | |
182 // Initialize the window text for Entry Point edit box | |
183 HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT); | |
184 ::SetWindowText(edit_box_entry_point, kDefaultEntryPoint_); | |
185 | |
186 // Initialize the window text for Log File edit box | |
187 HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); | |
188 ::SetWindowText(edit_box_log_file, kDefaultLogFile_); | |
189 | |
190 return static_cast<INT_PTR>(TRUE); | |
191 } | |
192 case WM_COMMAND: | |
193 // If the user presses the OK button (Launch) | |
194 if (LOWORD(wparam) == IDOK) { | |
195 if (host->OnLaunchDll(dialog)) { | |
196 if (host->SpawnTarget()) { | |
197 ::EndDialog(dialog, LOWORD(wparam)); | |
198 } | |
199 } | |
200 return static_cast<INT_PTR>(TRUE); | |
201 } else if (LOWORD(wparam) == IDCANCEL) { | |
202 // If the user presses the Cancel button | |
203 ::EndDialog(dialog, LOWORD(wparam)); | |
204 return static_cast<INT_PTR>(TRUE); | |
205 } else if (LOWORD(wparam) == IDC_BROWSE_DLL) { | |
206 // If the user presses the Browse button to look for a DLL | |
207 std::wstring dll_path = host->OnShowBrowseForDllDlg(dialog); | |
208 if (dll_path.length() > 0) { | |
209 // Initialize the window text for Log File edit box | |
210 HWND edit_box_dll_path = ::GetDlgItem(dialog, IDC_DLL_NAME); | |
211 ::SetWindowText(edit_box_dll_path, dll_path.c_str()); | |
212 } | |
213 return static_cast<INT_PTR>(TRUE); | |
214 } else if (LOWORD(wparam) == IDC_BROWSE_LOG) { | |
215 // If the user presses the Browse button to look for a log file | |
216 std::wstring log_path = host->OnShowBrowseForLogFileDlg(dialog); | |
217 if (log_path.length() > 0) { | |
218 // Initialize the window text for Log File edit box | |
219 HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); | |
220 ::SetWindowText(edit_box_log_file, log_path.c_str()); | |
221 } | |
222 return static_cast<INT_PTR>(TRUE); | |
223 } | |
224 | |
225 break; | |
226 } | |
227 | |
228 return static_cast<INT_PTR>(FALSE); | |
229 } | |
230 | |
231 MainUIWindow* MainUIWindow::FromWindow(HWND main_window) { | |
232 // We store a 'this' pointer using SetWindowLong in CreateMainWindowAndLoop | |
233 // so that we can retrieve it with this function later. This prevents us | |
234 // from having to define all the message handling functions (that we refer to | |
235 // in the window proc) as static | |
236 ::GetWindowLongPtr(main_window, GWLP_USERDATA); | |
237 return reinterpret_cast<MainUIWindow*>( | |
238 ::GetWindowLongPtr(main_window, GWLP_USERDATA)); | |
239 } | |
240 | |
241 BOOL MainUIWindow::OnCreate(HWND parent_window, LPCREATESTRUCT) { | |
242 // Create the listview that will the main app UI | |
243 list_view_ = ::CreateWindow(WC_LISTVIEW, // Class name | |
244 L"", // Window name | |
245 WS_CHILD | WS_VISIBLE | LVS_REPORT | | |
246 LVS_NOCOLUMNHEADER | WS_BORDER, | |
247 0, // x | |
248 0, // y | |
249 0, // width | |
250 0, // height | |
251 parent_window, // parent | |
252 NULL, // menu | |
253 ::GetModuleHandle(NULL), | |
254 0); // lpParam | |
255 | |
256 DCHECK(list_view_); | |
257 if (!list_view_) | |
258 return FALSE; | |
259 | |
260 LVCOLUMN list_view_column = {0}; | |
261 list_view_column.mask = LVCF_FMT | LVCF_WIDTH ; | |
262 list_view_column.fmt = LVCFMT_LEFT; | |
263 list_view_column.cx = 10000; // Maximum size of an entry in the list view. | |
264 ListView_InsertColumn(list_view_, 0, &list_view_column); | |
265 | |
266 // Set list view to show green font on black background | |
267 ListView_SetBkColor(list_view_, CLR_NONE); | |
268 ListView_SetTextColor(list_view_, RGB(0x0, 0x0, 0x0)); | |
269 ListView_SetTextBkColor(list_view_, CLR_NONE); | |
270 | |
271 return TRUE; | |
272 } | |
273 | |
274 void MainUIWindow::OnDestroy(HWND window) { | |
275 UNREFERENCED_PARAMETER(window); | |
276 | |
277 // Post a quit message because our application is over when the | |
278 // user closes this window. | |
279 ::PostQuitMessage(0); | |
280 } | |
281 | |
282 void MainUIWindow::OnSize(HWND window, UINT state, int cx, int cy) { | |
283 UNREFERENCED_PARAMETER(window); | |
284 UNREFERENCED_PARAMETER(state); | |
285 | |
286 // If we have a valid inner child, resize it to cover the entire | |
287 // client area of the main UI window. | |
288 if (list_view_) { | |
289 ::MoveWindow(list_view_, | |
290 0, // x | |
291 0, // y | |
292 cx, // width | |
293 cy, // height | |
294 TRUE); // repaint | |
295 } | |
296 } | |
297 | |
298 void MainUIWindow::OnPaint(HWND window) { | |
299 PAINTSTRUCT paintstruct; | |
300 ::BeginPaint(window, &paintstruct); | |
301 // add painting code here if required | |
302 ::EndPaint(window, &paintstruct); | |
303 } | |
304 | |
305 void MainUIWindow::OnFileExit() { | |
306 ::PostQuitMessage(0); | |
307 } | |
308 | |
309 void MainUIWindow::OnCommandsLaunch(HWND window) { | |
310 // User wants to see the Select DLL dialog box | |
311 ::DialogBox(instance_handle_, | |
312 MAKEINTRESOURCE(IDD_LAUNCH_DLL), | |
313 window, | |
314 SpawnTargetWndProc); | |
315 } | |
316 | |
317 bool MainUIWindow::OnLaunchDll(HWND dialog) { | |
318 HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME); | |
319 HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT); | |
320 HWND edit_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); | |
321 | |
322 wchar_t dll_path[MAX_PATH]; | |
323 wchar_t entry_point[MAX_PATH]; | |
324 wchar_t log_file[MAX_PATH]; | |
325 | |
326 int dll_name_len = ::GetWindowText(edit_box_dll_name, dll_path, MAX_PATH); | |
327 int entry_point_len = ::GetWindowText(edit_box_entry_point, | |
328 entry_point, MAX_PATH); | |
329 // Log file is optional (can be blank) | |
330 ::GetWindowText(edit_log_file, log_file, MAX_PATH); | |
331 | |
332 if (0 >= dll_name_len) { | |
333 ::MessageBox(dialog, | |
334 L"Please specify a DLL for the target to load", | |
335 L"No DLL specified", | |
336 MB_ICONERROR); | |
337 return false; | |
338 } | |
339 | |
340 if (GetFileAttributes(dll_path) == INVALID_FILE_ATTRIBUTES) { | |
341 ::MessageBox(dialog, | |
342 L"DLL specified was not found", | |
343 L"DLL not found", | |
344 MB_ICONERROR); | |
345 return false; | |
346 } | |
347 | |
348 if (0 >= entry_point_len) { | |
349 ::MessageBox(dialog, | |
350 L"Please specify an entry point for the DLL", | |
351 L"No entry point specified", | |
352 MB_ICONERROR); | |
353 return false; | |
354 } | |
355 | |
356 // store these values in the member variables for use in SpawnTarget | |
357 log_file_ = std::wstring(L"\"") + log_file + std::wstring(L"\""); | |
358 dll_path_ = dll_path; | |
359 entry_point_ = entry_point; | |
360 | |
361 return true; | |
362 } | |
363 | |
364 DWORD WINAPI MainUIWindow::ListenPipeThunk(void *param) { | |
365 return reinterpret_cast<MainUIWindow*>(param)->ListenPipe(); | |
366 } | |
367 | |
368 DWORD WINAPI MainUIWindow::WaitForTargetThunk(void *param) { | |
369 return reinterpret_cast<MainUIWindow*>(param)->WaitForTarget(); | |
370 } | |
371 | |
372 // Thread waiting for the target application to die. It displays | |
373 // a message in the list view when it happens. | |
374 DWORD MainUIWindow::WaitForTarget() { | |
375 WaitForSingleObject(target_.hProcess, INFINITE); | |
376 | |
377 DWORD exit_code = 0; | |
378 if (!GetExitCodeProcess(target_.hProcess, &exit_code)) { | |
379 exit_code = 0xFFFF; // Default exit code | |
380 } | |
381 | |
382 ::CloseHandle(target_.hProcess); | |
383 ::CloseHandle(target_.hThread); | |
384 | |
385 AddDebugMessage(L"Targed exited with return code %d", exit_code); | |
386 return 0; | |
387 } | |
388 | |
389 // Thread waiting for messages on the log pipe. It displays the messages | |
390 // in the listview. | |
391 DWORD MainUIWindow::ListenPipe() { | |
392 HANDLE logfile_handle = NULL; | |
393 ATL::CString file_to_open = log_file_.c_str(); | |
394 file_to_open.Remove(L'\"'); | |
395 if (file_to_open.GetLength()) { | |
396 logfile_handle = ::CreateFile(file_to_open.GetBuffer(), | |
397 GENERIC_WRITE, | |
398 FILE_SHARE_READ | FILE_SHARE_WRITE, | |
399 NULL, // Default security attributes | |
400 CREATE_ALWAYS, | |
401 FILE_ATTRIBUTE_NORMAL, | |
402 NULL); // No template | |
403 if (INVALID_HANDLE_VALUE == logfile_handle) { | |
404 AddDebugMessage(L"Failed to open \"%ls\" for logging. Error %d", | |
405 file_to_open.GetBuffer(), ::GetLastError()); | |
406 logfile_handle = NULL; | |
407 } | |
408 } | |
409 | |
410 const int kSizeBuffer = 1024; | |
411 BYTE read_buffer[kSizeBuffer] = {0}; | |
412 ATL::CStringA read_buffer_global; | |
413 ATL::CStringA string_to_print; | |
414 | |
415 DWORD last_error = 0; | |
416 while(last_error == ERROR_SUCCESS || last_error == ERROR_PIPE_LISTENING || | |
417 last_error == ERROR_NO_DATA) | |
418 { | |
419 DWORD read_data_length; | |
420 if (::ReadFile(pipe_handle_, | |
421 read_buffer, | |
422 kSizeBuffer - 1, // Max read size | |
423 &read_data_length, | |
424 NULL)) { // Not overlapped | |
425 if (logfile_handle) { | |
426 DWORD write_data_length; | |
427 ::WriteFile(logfile_handle, | |
428 read_buffer, | |
429 read_data_length, | |
430 &write_data_length, | |
431 FALSE); // Not overlapped | |
432 } | |
433 | |
434 // Append the new buffer to the current buffer | |
435 read_buffer[read_data_length] = NULL; | |
436 read_buffer_global += reinterpret_cast<char *>(read_buffer); | |
437 read_buffer_global.Remove(10); // Remove the CRs | |
438 | |
439 // If we completed a new line, output it | |
440 int endline = read_buffer_global.Find(13); // search for LF | |
441 while (-1 != endline) { | |
442 string_to_print = read_buffer_global; | |
443 string_to_print.Delete(endline, string_to_print.GetLength()); | |
444 read_buffer_global.Delete(0, endline); | |
445 | |
446 // print the line (with the ending LF) | |
447 OutputDebugStringA(string_to_print.GetBuffer()); | |
448 | |
449 // Remove the ending LF | |
450 read_buffer_global.Delete(0, 1); | |
451 | |
452 // Add the line to the log | |
453 AddDebugMessage(L"%S", string_to_print.GetBuffer()); | |
454 | |
455 endline = read_buffer_global.Find(13); | |
456 } | |
457 last_error = ERROR_SUCCESS; | |
458 } else { | |
459 last_error = GetLastError(); | |
460 Sleep(100); | |
461 } | |
462 } | |
463 | |
464 if (read_buffer_global.GetLength()) { | |
465 AddDebugMessage(L"%S", read_buffer_global.GetBuffer()); | |
466 } | |
467 | |
468 CloseHandle(pipe_handle_); | |
469 | |
470 if (logfile_handle) { | |
471 CloseHandle(logfile_handle); | |
472 } | |
473 | |
474 return 0; | |
475 } | |
476 | |
477 bool MainUIWindow::SpawnTarget() { | |
478 // Generate the pipe name | |
479 GUID random_id; | |
480 CoCreateGuid(&random_id); | |
481 | |
482 wchar_t log_pipe[MAX_PATH] = {0}; | |
483 wnsprintf(log_pipe, MAX_PATH - 1, | |
484 L"\\\\.\\pipe\\sbox_pipe_log_%lu_%lu_%lu_%lu", | |
485 random_id.Data1, | |
486 random_id.Data2, | |
487 random_id.Data3, | |
488 random_id.Data4); | |
489 | |
490 // We concatenate the four strings, add three spaces and a zero termination | |
491 // We use the resulting string as a param to CreateProcess (in SpawnTarget) | |
492 // Documented maximum for command line in CreateProcess is 32K (msdn) | |
493 size_t size_call = spawn_target_.length() + entry_point_.length() + | |
494 dll_path_.length() + wcslen(log_pipe) + 6; | |
495 if (32 * 1024 < (size_call * sizeof(wchar_t))) { | |
496 AddDebugMessage(L"The length of the arguments exceeded 32K. " | |
497 L"Aborting operation."); | |
498 return false; | |
499 } | |
500 | |
501 wchar_t * arguments = new wchar_t[size_call]; | |
502 wnsprintf(arguments, static_cast<int>(size_call), L"%ls %ls \"%ls\" %ls", | |
503 spawn_target_.c_str(), entry_point_.c_str(), | |
504 dll_path_.c_str(), log_pipe); | |
505 | |
506 arguments[size_call - 1] = L'\0'; | |
507 | |
508 sandbox::TargetPolicy* policy = broker_->CreatePolicy(); | |
509 policy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0); | |
510 policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, | |
511 sandbox::USER_LOCKDOWN); | |
512 policy->SetAlternateDesktop(true); | |
513 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); | |
514 | |
515 // Set the rule to allow the POC dll to be loaded by the target. Note that | |
516 // the rule allows 'all access' to the DLL, which could mean that the target | |
517 // could modify the DLL on disk. | |
518 policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, | |
519 sandbox::TargetPolicy::FILES_ALLOW_ANY, dll_path_.c_str()); | |
520 | |
521 sandbox::ResultCode result = broker_->SpawnTarget(spawn_target_.c_str(), | |
522 arguments, policy, | |
523 &target_); | |
524 | |
525 policy->Release(); | |
526 policy = NULL; | |
527 | |
528 bool return_value = false; | |
529 if (sandbox::SBOX_ALL_OK != result) { | |
530 AddDebugMessage( | |
531 L"Failed to spawn target %ls w/args (%ls), sandbox error code: %d", | |
532 spawn_target_.c_str(), arguments, result); | |
533 return_value = false; | |
534 } else { | |
535 | |
536 DWORD thread_id; | |
537 ::CreateThread(NULL, // Default security attributes | |
538 NULL, // Default stack size | |
539 &MainUIWindow::WaitForTargetThunk, | |
540 this, | |
541 0, // No flags | |
542 &thread_id); | |
543 | |
544 pipe_handle_ = ::CreateNamedPipe(log_pipe, | |
545 PIPE_ACCESS_INBOUND | WRITE_DAC, | |
546 PIPE_TYPE_MESSAGE | PIPE_NOWAIT, | |
547 1, // Number of instances. | |
548 512, // Out buffer size. | |
549 512, // In buffer size. | |
550 NMPWAIT_USE_DEFAULT_WAIT, | |
551 NULL); // Default security descriptor | |
552 | |
553 if (INVALID_HANDLE_VALUE == pipe_handle_) | |
554 AddDebugMessage(L"Failed to create pipe. Error %d", ::GetLastError()); | |
555 | |
556 if (!sandbox::AddKnownSidToKernelObject(pipe_handle_, WinWorldSid, | |
557 FILE_ALL_ACCESS)) | |
558 AddDebugMessage(L"Failed to set security on pipe. Error %d", | |
559 ::GetLastError()); | |
560 | |
561 ::CreateThread(NULL, // Default security attributes | |
562 NULL, // Default stack size | |
563 &MainUIWindow::ListenPipeThunk, | |
564 this, | |
565 0, // No flags | |
566 &thread_id); | |
567 | |
568 ::ResumeThread(target_.hThread); | |
569 | |
570 AddDebugMessage(L"Successfully spawned target w/args (%ls)", arguments); | |
571 return_value = true; | |
572 } | |
573 | |
574 delete[] arguments; | |
575 return return_value; | |
576 } | |
577 | |
578 std::wstring MainUIWindow::OnShowBrowseForDllDlg(HWND owner) { | |
579 wchar_t filename[MAX_PATH]; | |
580 wcscpy_s(filename, MAX_PATH, L""); | |
581 | |
582 OPENFILENAMEW file_info = {0}; | |
583 file_info.lStructSize = sizeof(file_info); | |
584 file_info.hwndOwner = owner; | |
585 file_info.lpstrFile = filename; | |
586 file_info.nMaxFile = MAX_PATH; | |
587 file_info.lpstrFilter = L"DLL files (*.dll)\0*.dll\0All files\0*.*\0\0\0"; | |
588 | |
589 file_info.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; | |
590 | |
591 if (GetOpenFileName(&file_info)) { | |
592 return file_info.lpstrFile; | |
593 } | |
594 | |
595 return L""; | |
596 } | |
597 | |
598 std::wstring MainUIWindow::OnShowBrowseForLogFileDlg(HWND owner) { | |
599 wchar_t filename[MAX_PATH]; | |
600 wcscpy_s(filename, MAX_PATH, L""); | |
601 | |
602 OPENFILENAMEW file_info = {0}; | |
603 file_info.lStructSize = sizeof(file_info); | |
604 file_info.hwndOwner = owner; | |
605 file_info.lpstrFile = filename; | |
606 file_info.nMaxFile = MAX_PATH; | |
607 file_info.lpstrFilter = L"Log file (*.txt)\0*.txt\0All files\0*.*\0\0\0"; | |
608 | |
609 file_info.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; | |
610 | |
611 if (GetSaveFileName(&file_info)) { | |
612 return file_info.lpstrFile; | |
613 } | |
614 | |
615 return L""; | |
616 } | |
617 | |
618 void MainUIWindow::AddDebugMessage(const wchar_t* format, ...) { | |
619 DCHECK(format); | |
620 if (!format) | |
621 return; | |
622 | |
623 const int kMaxDebugBuffSize = 1024; | |
624 | |
625 va_list arg_list; | |
626 _crt_va_start(arg_list, format); | |
627 | |
628 wchar_t text[kMaxDebugBuffSize + 1]; | |
629 vswprintf_s(text, kMaxDebugBuffSize, format, arg_list); | |
630 text[kMaxDebugBuffSize] = L'\0'; | |
631 | |
632 InsertLineInListView(text); | |
633 } | |
634 | |
635 | |
636 void MainUIWindow::InsertLineInListView(wchar_t* debug_message) { | |
637 DCHECK(debug_message); | |
638 if (!debug_message) | |
639 return; | |
640 | |
641 // Prepend the time to the message | |
642 const int kSizeTime = 100; | |
643 size_t size_message_with_time = wcslen(debug_message) + kSizeTime; | |
644 wchar_t * message_time = new wchar_t[size_message_with_time]; | |
645 | |
646 time_t time_temp; | |
647 time_temp = time(NULL); | |
648 | |
649 struct tm time = {0}; | |
650 localtime_s(&time, &time_temp); | |
651 | |
652 size_t return_code; | |
653 return_code = wcsftime(message_time, kSizeTime, L"[%H:%M:%S] ", &time); | |
654 | |
655 wcscat_s(message_time, size_message_with_time, debug_message); | |
656 | |
657 // We add the debug message to the top of the listview | |
658 LVITEM item; | |
659 item.iItem = ListView_GetItemCount(list_view_); | |
660 item.iSubItem = 0; | |
661 item.mask = LVIF_TEXT | LVIF_PARAM; | |
662 item.pszText = message_time; | |
663 item.lParam = 0; | |
664 | |
665 ListView_InsertItem(list_view_, &item); | |
666 | |
667 delete[] message_time; | |
668 } | |
OLD | NEW |