0
|
1 /**
|
|
2 A window used to display the data from a CrashInfo struct generated by the
|
|
3 runtime's crash handler.
|
|
4
|
|
5 TODO:
|
|
6 * Send report - need SMTP implementation
|
|
7 * Save report
|
|
8
|
|
9 Authors:
|
|
10 Jeremie Pelletier
|
|
11
|
|
12 License:
|
|
13 Public Domain
|
|
14 */
|
|
15 module dbg.ui.CrashWindow;
|
|
16
|
|
17 import std.c.string;
|
|
18 import dlib.CrashHandler;
|
|
19 import dbg.Debug : SystemException;
|
|
20
|
|
21 import std.stdio;
|
|
22
|
|
23 private enum {
|
|
24 ReportWindowWidth = 640,
|
|
25 ReportWindowHeight = 480,
|
|
26 ReportWindowMinWidth = 320,
|
|
27 ReportWindowMinHeight = 240,
|
|
28 ReportWindowTitle = "Unhandled exception!"
|
|
29 }
|
|
30
|
|
31 // ----------------------------------------------------------------------------
|
|
32 // W i n 3 2
|
|
33 // ----------------------------------------------------------------------------
|
|
34
|
|
35 version(Windows) {
|
|
36
|
|
37 import win32.windows;
|
|
38
|
|
39 private enum {
|
|
40 ID_LABEL = 100,
|
|
41 ID_SAVE_BTN = 101,
|
|
42 ID_SEND_BTN = 102,
|
|
43 ID_CLOSE_BTN = 103,
|
|
44 ID_REPORT = 104
|
|
45 }
|
|
46
|
|
47 enum CLEARTYPE_QUALITY = 5;
|
|
48 enum LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20;
|
|
49
|
|
50 /**
|
|
51 Create the crash window for the given crash information, the routine will
|
|
52 return when the window is closed.
|
|
53 */
|
|
54 void ShowCrashWindow(CrashInfo* crashInfo)
|
|
55 in {
|
|
56 assert(crashInfo);
|
|
57 }
|
|
58 body {
|
|
59 try {
|
|
60
|
|
61 HINSTANCE inst = GetModuleHandle(null);
|
|
62
|
|
63 WNDCLASSEXA wc;
|
|
64 wc.cbSize = WNDCLASSEX.sizeof;
|
|
65 wc.lpfnWndProc = &ReportWndProc;
|
|
66 wc.hInstance = inst;
|
|
67 wc.hIcon = cast(HICON)LoadImage(HINSTANCE.init, MAKEINTRESOURCE(OIC_ERROR), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
|
|
68 wc.hCursor = cast(HCURSOR)LoadImage(HINSTANCE.init, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
|
|
69 wc.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
|
|
70 wc.lpszClassName = "CrashWindowClass";
|
|
71
|
|
72 if(!RegisterClassExA(&wc)) SystemException();
|
|
73 scope(exit) if(!UnregisterClassA("CrashWindowClass", inst)) SystemException();
|
|
74
|
|
75 RECT rc = void;
|
|
76 GetClientRect(GetDesktopWindow(), &rc);
|
|
77
|
|
78 writeln(crashInfo.toString);
|
|
79
|
|
80 HWND wnd = CreateWindowExA(
|
|
81 WS_EX_WINDOWEDGE,
|
|
82 "CrashWindowClass", ReportWindowTitle,
|
|
83 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
84 (rc.right >> 1) - (ReportWindowWidth >> 1), (rc.bottom >> 1) - (ReportWindowHeight >> 1),
|
|
85 ReportWindowWidth, ReportWindowHeight,
|
|
86 HWND.init, HMENU.init, inst, cast(void*)crashInfo.toString.ptr
|
|
87 );
|
|
88 if(!wnd) SystemException();
|
|
89
|
|
90 MSG msg = void;
|
|
91 while(GetMessage(&msg, HWND.init, 0, 0)) {
|
|
92 TranslateMessage(&msg);
|
|
93 DispatchMessage(&msg);
|
|
94 }
|
|
95
|
|
96 } // try
|
|
97 catch(Throwable e) {
|
|
98 MessageBoxA(HWND.init, (e.toString ~ '\0').ptr, "Crash Window Error!", MB_ICONERROR | MB_OK);
|
|
99 }
|
|
100 }
|
|
101
|
|
102 private:
|
|
103
|
|
104 __gshared HWND saveButton, sendButton, closeButton, reportField;
|
|
105
|
|
106 extern(Windows)
|
|
107 LRESULT ReportWndProc(HWND wnd, uint msg, WPARAM w, LPARAM l) {
|
|
108 try {
|
|
109
|
|
110 switch(msg) {
|
|
111 case WM_CREATE:
|
|
112 HINSTANCE inst = cast(HINSTANCE).GetModuleHandle(null);
|
|
113 CREATESTRUCT* cs = cast(CREATESTRUCT*)l;
|
|
114
|
|
115 LOGFONTA lf;
|
|
116 lf.lfHeight = 15;
|
|
117 lf.lfWeight = FW_REGULAR;
|
|
118 lf.lfCharSet = DEFAULT_CHARSET;
|
|
119 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
|
120 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
121 lf.lfQuality = CLEARTYPE_QUALITY;
|
|
122 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
|
123
|
|
124 HFONT font = .CreateFontIndirectA(&lf);
|
|
125 if(!font) SystemException();
|
|
126
|
|
127 HINSTANCE iconMod = LoadLibraryExA("shell32.dll", null,
|
|
128 DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
|
|
129
|
|
130 HWND CreateButton(string caption, uint id, ushort iconId) {
|
|
131 HWND ret = CreateWindowExA(
|
|
132 0, "BUTTON", caption.ptr, WS_CHILD | WS_VISIBLE,
|
|
133 0, 0, 0, 0, wnd, cast(HMENU)id, inst, null
|
|
134 );
|
|
135 if(!ret) SystemException();
|
|
136
|
|
137 SendMessageA(ret, WM_SETFONT, cast(WPARAM)font, 0);
|
|
138
|
|
139 if(iconMod) {
|
|
140 HANDLE icon = LoadImage(iconMod, MAKEINTRESOURCE(iconId), IMAGE_ICON, 24, 24, 0);
|
|
141 if(icon) SendMessageA(ret, BM_SETIMAGE, IMAGE_ICON, cast(uint)icon);
|
|
142 }
|
|
143
|
|
144 return ret;
|
|
145 }
|
|
146
|
|
147 saveButton = CreateButton("Save Report", ID_SAVE_BTN, 7);
|
|
148 sendButton = CreateButton("Send Report", ID_SEND_BTN, 27);
|
|
149 closeButton = CreateButton("Close", ID_CLOSE_BTN, 28);
|
|
150
|
|
151 if(iconMod) FreeLibrary(cast(HMODULE)iconMod);
|
|
152
|
|
153 enum ReportFont = "Courier New\0";
|
|
154 lf.lfHeight = 14;
|
|
155 lf.lfFaceName[0 .. ReportFont.length] = ReportFont;
|
|
156
|
|
157 font = CreateFontIndirectA(&lf);
|
|
158 if(!font) SystemException();
|
|
159
|
|
160 reportField = CreateWindowExA(
|
|
161 WS_EX_CLIENTEDGE, "EDIT", null,
|
|
162 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE |
|
|
163 ES_READONLY | ES_MULTILINE | ES_AUTOVSCROLL,
|
|
164 0, 0, 0, 0, wnd, cast(HMENU)ID_REPORT, inst, null
|
|
165 );
|
|
166 if(!reportField) SystemException();
|
|
167
|
|
168 SendMessageA(reportField, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(5, 5));
|
|
169 SendMessageA(reportField, WM_SETFONT, cast(WPARAM)font, 0);
|
|
170 SendMessageA(reportField, WM_SETTEXT, 0, cast(LPARAM)cs.lpCreateParams);
|
|
171
|
|
172 break;
|
|
173
|
|
174 case WM_DESTROY:
|
|
175 PostQuitMessage(0);
|
|
176 break;
|
|
177
|
|
178 case WM_GETMINMAXINFO:
|
|
179 MINMAXINFO* mm = cast(MINMAXINFO*)l;
|
|
180 mm.ptMinTrackSize.x = ReportWindowMinWidth;
|
|
181 mm.ptMinTrackSize.y = ReportWindowMinHeight;
|
|
182 break;
|
|
183
|
|
184 case WM_SIZE:
|
|
185 int width = LOWORD(l), halfWidth = width >> 1;
|
|
186 int height = HIWORD(l);
|
|
187
|
|
188 enum {
|
|
189 BtnWidth = 125,
|
|
190 BtnHeight = 35,
|
|
191 NumBtns = 3,
|
|
192 Pad = 10,
|
|
193 ReportHPad = Pad * 2,
|
|
194 ReportVPad = BtnHeight + Pad * 3,
|
|
195 BtnVPad = BtnHeight + Pad,
|
|
196 BtnHalfWidth = (BtnWidth * NumBtns + (Pad * (NumBtns - 1))) >> 1
|
|
197 }
|
|
198
|
|
199 if(!MoveWindow(reportField, Pad, Pad, width - ReportHPad, height - ReportVPad, true))
|
|
200 SystemException();
|
|
201
|
|
202 void Move(HWND wnd, int i) {
|
|
203 if(!MoveWindow(wnd, halfWidth - BtnHalfWidth + BtnWidth * i + Pad * i,
|
|
204 height - BtnVPad, BtnWidth, BtnHeight, true))
|
|
205 SystemException();
|
|
206 }
|
|
207
|
|
208 Move(saveButton, 0);
|
|
209 Move(sendButton, 1);
|
|
210 Move(closeButton, 2);
|
|
211
|
|
212 break;
|
|
213
|
|
214 case WM_COMMAND:
|
|
215 if(HIWORD(w) != BN_CLICKED) break;
|
|
216
|
|
217 int id = LOWORD(w);
|
|
218
|
|
219 // GetSaveFileName fails on win7.. no idea why
|
|
220 if(id == ID_SAVE_BTN) {
|
|
221 /*char[256] path = void;
|
|
222 path[0 .. 11] = "Report.txt\0";
|
|
223
|
|
224 OPENFILENAMEA ofn;
|
|
225 ofn.lStructSize = OPENFILENAME.sizeof;
|
|
226 ofn.hwndOwner = wnd;
|
|
227 ofn.lpstrFilter = "Text File\0*.txt\0\0";
|
|
228 ofn.lpstrFile = path.ptr;
|
|
229 ofn.nMaxFile = path.length;
|
|
230 ofn.Flags = OFN_OVERWRITEPROMPT;
|
|
231
|
|
232 try {
|
|
233 if(!GetSaveFileNameA(&ofn)) SystemException();
|
|
234
|
|
235 uint len = strlen(path.ptr);
|
|
236 if(path[len-4 .. len] != ".txt") path[len .. len + 5] = ".txt\0";
|
|
237
|
|
238 HANDLE file = CreateFileA(path.ptr, GENERIC_WRITE, 0,
|
|
239 null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null);
|
|
240
|
|
241 if(file == INVALID_HANDLE_VALUE) SystemException();
|
|
242 scope(exit) if(!CloseHandle(file)) SystemException();
|
|
243
|
|
244 char* text;
|
|
245 SendMessageA(reportField, WM_GETTEXT, 0, cast(LPARAM)text);
|
|
246 len = strlen(text);
|
|
247
|
|
248 uint written = void;
|
|
249 if(!WriteFile(file, text, len, &written, null))
|
|
250 SystemException();
|
|
251
|
|
252 if(written != len) throw new SystemException("Couldn't write entire data.");
|
|
253
|
|
254 id = ID_CLOSE_BTN;
|
|
255 }
|
|
256 catch(SystemException e) {
|
|
257 MessageBoxA(wnd, (e.toString ~ '\0').ptr, "Error!", MB_OK | MB_ICONERROR);
|
|
258 }*/
|
|
259
|
|
260 MessageBoxA(wnd, "TODO", "Error!", MB_OK | MB_ICONERROR);
|
|
261 }
|
|
262
|
|
263 if(id == ID_SEND_BTN)
|
|
264 MessageBoxA(wnd, "TODO", "Error!", MB_OK | MB_ICONERROR);
|
|
265
|
|
266 if(id == ID_CLOSE_BTN)
|
|
267 SendMessageA(wnd, WM_CLOSE, 0, 0);
|
|
268
|
|
269 break;
|
|
270
|
|
271 default:
|
|
272 return DefWindowProcA(wnd, msg, w, l);
|
|
273 }
|
|
274
|
|
275 } // try
|
|
276 catch(Exception e) {
|
|
277 MessageBoxA(HWND.init, (e.toString ~ '\0').ptr, "Crash Window Handler Error!", MB_ICONERROR | MB_OK);
|
|
278 PostQuitMessage(0);
|
|
279 }
|
|
280
|
|
281 return 0;
|
|
282 }
|
|
283
|
|
284 } // version(Windows)
|
|
285
|
|
286 else version(Gnome) {
|
|
287
|
|
288 import std.c.stdio;
|
|
289
|
|
290 import ext.Gnome.gtk;
|
|
291 import ext.Gnome.gobject;
|
|
292
|
|
293 void ErrorGUI(in ErrorReport* report) {
|
|
294 int argc;
|
|
295 if(!gtk_init_check(&argc, null)) {
|
|
296 printf("gtk failed!\n");
|
|
297 Pause;
|
|
298 }
|
|
299
|
|
300 void SetSignal(A, B)(A* widget, string signal, B callback) {
|
|
301 g_signal_connect(cast(void*)widget, signal.ptr, cast(GCallback)callback, null);
|
|
302 }
|
|
303
|
|
304 // Create the report window
|
|
305 GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
306 gtk_window_set_title(cast(GtkWindow*)window, ReportWindowTitle);
|
|
307 gtk_window_set_default_size(cast(GtkWindow*)window, ReportWindowWidth, ReportWindowHeight);
|
|
308 gtk_window_set_icon_name(cast(GtkWindow*)window, GTK_STOCK_DIALOG_ERROR);
|
|
309
|
|
310 SetSignal(window, "destroy", >k_main_quit);
|
|
311
|
|
312 // Create the root box
|
|
313 GtkWidget* vbox = gtk_vbox_new(false, 0);
|
|
314 gtk_container_add(cast(GtkContainer*)window, vbox);
|
|
315
|
|
316 // Create the report edit
|
|
317 GtkWidget* view = gtk_text_view_new();
|
|
318 gtk_text_view_set_editable(cast(GtkTextView*)view, false);
|
|
319 gtk_text_view_set_cursor_visible(cast(GtkTextView*)view, false);
|
|
320
|
|
321 GtkTextBuffer* buffer = gtk_text_view_get_buffer(cast(GtkTextView*)view);
|
|
322 gtk_text_buffer_set_text(buffer, report.dumpText.ptr, -1);
|
|
323
|
|
324 gtk_box_pack_start(cast(GtkBox*)vbox, view, true, true, 0);
|
|
325
|
|
326 // Create the buttons box
|
|
327 GtkWidget* hbox = gtk_hbutton_box_new();
|
|
328 gtk_box_set_spacing(cast(GtkBox*)hbox, 10);
|
|
329 gtk_button_box_set_layout(cast(GtkButtonBox*)hbox, GTK_BUTTONBOX_CENTER);
|
|
330 gtk_box_pack_start(cast(GtkBox*)vbox, hbox, false, true, 10);
|
|
331
|
|
332 // Create the buttons
|
|
333 GtkWidget* CreateButton(B)(string label, string stockId, B callback) {
|
|
334 GtkWidget* button = gtk_button_new_with_label(label.ptr);
|
|
335
|
|
336 GtkWidget* image = gtk_image_new_from_stock(stockId.ptr, GTK_ICON_SIZE_BUTTON);
|
|
337 gtk_button_set_image(cast(GtkButton*)button, image);
|
|
338
|
|
339 gtk_container_add(cast(GtkContainer*)hbox, button);
|
|
340
|
|
341 if(callback) SetSignal(button, "clicked", callback);
|
|
342
|
|
343 return button;
|
|
344 }
|
|
345
|
|
346 CreateButton("Save Report", GTK_STOCK_SAVE_AS, &OnClickSave);
|
|
347 CreateButton("Send Report", GTK_STOCK_CONNECT, &OnClickSend);
|
|
348 GtkWidget* close = CreateButton("Close", GTK_STOCK_CLOSE, null);
|
|
349
|
|
350 g_signal_connect_swapped(cast(void*)close, "clicked",
|
|
351 cast(GCallback)(>k_widget_destroy), cast(void*)(window));
|
|
352
|
|
353 // Display the window and run the main loop
|
|
354 gtk_widget_show_all(window);
|
|
355 gtk_main();
|
|
356 }
|
|
357
|
|
358 extern(C):
|
|
359
|
|
360 void OnClickSave(GtkButton* button, gpointer user_data) {
|
|
361 // TODO
|
|
362 }
|
|
363
|
|
364 void OnClickSend(GtkButton* button, gpointer user_data) {
|
|
365 // TODO
|
|
366 }
|
|
367
|
|
368 } // version(Gnome)
|
|
369 else static assert(0); |