1 /** DGui project file.
2 
3 Copyright: Trogu Antonio Davide 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Trogu Antonio Davide
8 */
9 module dguihub.application;
10 
11 pragma(lib, "gdi32.lib");
12 pragma(lib, "comdlg32.lib");
13 
14 import std.path;
15 
16 private import dguihub.core.winapi;
17 
18 private import dguihub.core.utils;
19 
20 private import dguihub.richtextbox;
21 
22 private import dguihub.form;
23 
24 private import dguihub.button;
25 
26 private import dguihub.label;
27 
28 private import std.utf : toUTFz;
29 
30 private import std.file;
31 
32 private import std.conv;
33 public import dguihub.resources;
34 
35 private enum {
36    info = "Exception Information:",
37    xpManifestFile = "dguihub.xml.manifest",
38    errMsg = "An application exception has occured.\r\n1) Click \"Ignore\" to continue (The program can be unstable).\r\n2) Click \"Quit\" to exit.\r\n",
39    xpManifest = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` ~ "\r\n"
40       ~ `<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">`
41       ~ "\r\n" ~ `<assemblyIdentity` ~ "\r\n"
42       ~ `version="1.0.0.0"` ~ "\r\n" ~ `processorArchitecture="X86"`
43       ~ "\r\n" ~ `name="client"` ~ "\r\n" ~ `type="win32"` ~ "\r\n" ~ `/>` ~ "\r\n"
44       ~ `<description></description>` ~ "\r\n" ~ "\r\n"
45       ~ `<!-- Enable Windows XP and higher themes with common controls -->`
46       ~ "\r\n" ~ `<dependency>` ~ "\r\n" ~ `<dependentAssembly>` ~ "\r\n" ~ `<assemblyIdentity` ~ "\r\n"
47       ~ `type="win32"` ~ "\r\n" ~ `name="Microsoft.Windows.Common-Controls"` ~ "\r\n"
48       ~ `version="6.0.0.0"` ~ "\r\n" ~ `processorArchitecture="X86"` ~ "\r\n" ~ `publicKeyToken="6595b64144ccf1df"`
49       ~ "\r\n" ~ `language="*"` ~ "\r\n" ~ `/>` ~ "\r\n" ~ `</dependentAssembly>`
50       ~ "\r\n" ~ `</dependency>` ~ "\r\n" ~ "\r\n"
51       ~ `<!-- Disable Windows Vista UAC compatibility heuristics -->` ~ "\r\n"
52       ~ `<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">` ~ "\r\n" ~ `<security>`
53       ~ "\r\n" ~ `<requestedPrivileges>` ~ "\r\n" ~ `<requestedExecutionLevel level="asInvoker"/>`
54       ~ "\r\n" ~ `</requestedPrivileges>` ~ "\r\n" ~ `</security>` ~ "\r\n" ~ `</trustInfo> ` ~ "\r\n" ~ "\r\n"
55       ~ `<!-- Enable Windows Vista-style font scaling on Vista -->` ~ "\r\n"
56       ~ `<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">` ~ "\r\n"
57       ~ `<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">`
58       ~ "\r\n" ~ `<dpiAware>true</dpiAware>` ~ "\r\n" ~ `</asmv3:windowsSettings>`
59       ~ "\r\n" ~ `</asmv3:application>` ~ "\r\n" ~ `</assembly>` ~ "\r\n",
60 }
61 private alias extern (Windows) BOOL function(HANDLE hActCtx, ULONG_PTR* lpCookie) ActivateActCtxProc;
62 private alias extern (Windows) HANDLE function(ACTCTXW* pActCtx) CreateActCtxWProc;
63 private alias extern (Windows) bool function(INITCOMMONCONTROLSEX*) InitCommonControlsExProc;
64 
65 /**
66    The _Application class manage the whole program, it can be used for load embedded resources,
67    close the program, get the current path and so on.
68    Internally in initialize manifest (if available), DLLs, and it handle exceptions showing a window with exception information.
69   */
70 class Application {
71    private static class ExceptionForm : Form {
72       public this(Throwable e) {
73          this.text = "An Exception was thrown...";
74          this.size = Size(400, 220);
75          this.controlBox = false;
76          this.startPosition = FormStartPosition.centerParent;
77          this.formBorderStyle = FormBorderStyle.fixedDialog;
78 
79          this._lblHead = new Label();
80          this._lblHead.alignment = TextAlignment.middle | TextAlignment.left;
81          this._lblHead.foreColor = Color(0xB4, 0x00, 0x00);
82          this._lblHead.dock = DockStyle.top;
83          this._lblHead.height = 50;
84          this._lblHead.text = errMsg;
85          this._lblHead.parent = this;
86 
87          this._lblInfo = new Label();
88          this._lblInfo.alignment = TextAlignment.middle | TextAlignment.left;
89          this._lblInfo.dock = DockStyle.top;
90          this._lblInfo.height = 20;
91          this._lblInfo.text = info;
92          this._lblInfo.parent = this;
93 
94          this._rtfText = new RichTextBox();
95          this._rtfText.borderStyle = BorderStyle.fixed3d;
96          this._rtfText.dock = DockStyle.top;
97          this._rtfText.height = 90;
98          this._rtfText.backColor = SystemColors.colorButtonFace;
99          this._rtfText.scrollBars = true;
100          this._rtfText.readOnly = true;
101          this._rtfText.text = e.msg;
102          this._rtfText.parent = this;
103 
104          this._btnQuit = new Button();
105          this._btnQuit.bounds = Rect(310, 164, 80, 23);
106          this._btnQuit.dialogResult = DialogResult.abort;
107          this._btnQuit.text = "Quit";
108          this._btnQuit.parent = this;
109 
110          this._btnIgnore = new Button();
111          this._btnIgnore.bounds = Rect(225, 164, 80, 23);
112          this._btnIgnore.dialogResult = DialogResult.ignore;
113          this._btnIgnore.text = "Ignore";
114          this._btnIgnore.parent = this;
115       }
116 
117       private RichTextBox _rtfText;
118       private Label _lblHead;
119       private Label _lblInfo;
120       private Button _btnIgnore;
121       private Button _btnQuit;
122    }
123 
124    /// Static constructor (it enable the manifest, if available)
125    public static this() {
126       Application.enableManifest(); //Enable Manifest (if available)
127    }
128 
129    /*
130 	      This method calls GetModuleHandle() API
131 
132 		Returns:
133 			HINSTANCE of the program
134 	  */
135    @property public static HINSTANCE instance() {
136       return getHInstance();
137    }
138 
139    /**
140 		Returns:
141 			String value of the executable path ($(B including) the executable name)
142 	   */
143    @property public static string executablePath() {
144       return getExecutablePath();
145    }
146 
147    /**
148 	   This method calls GetTempPath() API
149 
150 		Returns:
151 			String value of the system's TEMP directory
152 	   */
153    @property public static string tempPath() {
154       return dguihub.core.utils.getTempPath();
155    }
156 
157    /**
158 	   Returns:
159 		String value of the executable path ($(B without) the executable name)
160 	   */
161    @property public static string startupPath() {
162       return getStartupPath();
163    }
164 
165    /**
166 	   This property allows to load embedded _resources.
167 
168 		Returns:
169 			The Instance of reource object
170 
171 		See_Also:
172 			Resources Class
173 	 */
174    @property public static Resources resources() {
175       return Resources.instance;
176    }
177 
178    /**
179 	   Internal method that enable XP Manifest (if available)
180 	 */
181    private static void enableManifest() {
182       HMODULE hKernel32 = getModuleHandle("kernel32.dll");
183 
184       if (hKernel32) {
185          CreateActCtxWProc createActCtx = cast(CreateActCtxWProc)GetProcAddress(hKernel32,
186                "CreateActCtxW");
187 
188          if (createActCtx) // Don't break Win2k compatibility
189          {
190             string temp = dguihub.core.utils.getTempPath();
191             ActivateActCtxProc activateActCtx = cast(ActivateActCtxProc)GetProcAddress(hKernel32,
192                   "ActivateActCtx");
193             temp = std.path.buildPath(temp, xpManifestFile);
194             std.file.write(temp, xpManifest);
195 
196             ACTCTXW actx;
197 
198             actx.cbSize = ACTCTXW.sizeof;
199             actx.dwFlags = 0;
200             actx.lpSource = toUTFz!(wchar*)(temp);
201 
202             HANDLE hActx = createActCtx(&actx);
203 
204             if (hActx != INVALID_HANDLE_VALUE) {
205                ULONG_PTR cookie;
206                activateActCtx(hActx, &cookie);
207             }
208 
209             if (std.file.exists(temp)) {
210                std.file.remove(temp);
211             }
212          }
213       }
214 
215       initCommonControls();
216    }
217 
218    /**
219 	  Internal method that loads ComCtl32 DLL
220 	  */
221    private static void initCommonControls() {
222       INITCOMMONCONTROLSEX icc = void;
223 
224       icc.dwSize = INITCOMMONCONTROLSEX.sizeof;
225       icc.dwICC = 0xFFFFFFFF;
226 
227       HMODULE hComCtl32 = loadLibrary("comctl32.dll");
228 
229       if (hComCtl32) {
230          InitCommonControlsExProc iccex = cast(InitCommonControlsExProc)GetProcAddress(hComCtl32,
231                "InitCommonControlsEx");
232 
233          if (iccex) {
234             iccex(&icc);
235          }
236       }
237    }
238 
239    /**
240 	  Start the program and handles handles Exception
241 	  Params:
242 		mainForm = The Application's main form
243 
244 	  Returns:
245 		Zero
246 	  */
247    private static int doRun(Form mainForm) {
248       mainForm.show();
249       return 0;
250    }
251 
252    /**
253 	  Start the program and adds onClose() event at the MainForm
254 	  Params:
255 		mainForm = The Application's main form
256 
257 	  Returns:
258 		Zero
259 	  */
260    public static int run(Form mainForm) {
261       mainForm.close.attach(&onMainFormClose);
262       return Application.doRun(mainForm);
263    }
264 
265    /**
266 	  Close the program.
267 	  Params:
268 		exitCode = Exit code of the program (usually is 0)
269 	  */
270    public static void exit(int exitCode = 0) {
271       ExitProcess(exitCode);
272    }
273 
274    /**
275 	  When an exception was thrown, the _Application class call this method
276 	  showing the exception information, the user has the choice to continue the
277 	  application or terminate it.
278 
279 	  Returns:
280 		A DialogResult enum that contains the button clicked by the user (ignore or abort)
281 	  */
282    package static DialogResult showExceptionForm(Throwable e) {
283       ExceptionForm ef = new ExceptionForm(e);
284       return ef.showDialog();
285    }
286 
287    /**
288      Close _Application event attached (internally) at the main form
289     */
290    private static void onMainFormClose(Control sender, EventArgs e) {
291       Application.exit();
292    }
293 }