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