1 /*
2    Copyright (C) 2004-2007 Christopher E. Miller
3 
4    This software is provided 'as-is', without any express or implied
5    warranty.  In no event will the authors be held liable for any damages
6    arising from the use of this software.
7 
8    Permission is granted to anyone to use this software for any purpose,
9    including commercial applications, and to alter it and redistribute it
10    freely, subject to the following restrictions:
11 
12    1. The origin of this software must not be misrepresented; you must not
13       claim that you wrote the original software. If you use this software
14       in a product, an acknowledgment in the product documentation would be
15       appreciated but is not required.
16    2. Altered source versions must be plainly marked as such, and must not be
17       misrepresented as being the original software.
18    3. This notice may not be removed or altered from any source distribution.
19 */
20 
21 
22 module dfl.internal.utf;
23 
24 private import dfl.internal.dlib, dfl.internal.clib;
25 
26 private import dfl.internal.winapi;
27 
28 
29 private import std.windows.charset;
30 
31 
32 version(DFL_NO_D2_AND_ABOVE) {
33 }
34 else {
35    version(D_Version2) {
36       version = DFL_D2_AND_ABOVE;
37    }
38    else version(D_Version3) {
39       version = DFL_D3_AND_ABOVE;
40       version = DFL_D2_AND_ABOVE;
41    }
42 }
43 
44 // Do not support Win9x.
45 version = DFL_UNICODE;
46 enum useUnicode = true;
47 
48 package:
49 
50 version(DFL_LOAD_INTERNAL_LIBS) {
51    alias LoadLibraryA initInternalLib;
52 }
53 else {
54    version = DFL_GET_INTERNAL_LIBS;
55 
56    alias GetModuleHandleA initInternalLib;
57 }
58 
59 
60 HMODULE _user32, _kernel32, _advapi32, _gdi32;
61 
62 package @property HMODULE advapi32() nothrow { // getter
63    // advapi32 generally always delay loads.
64    if(!_advapi32) {
65       _advapi32 = LoadLibraryA("advapi32.dll");
66    }
67    return _advapi32;
68 }
69 
70 package @property HMODULE gdi32() nothrow { // getter
71    // gdi32 sometimes delay loads.
72    version(DFL_GET_INTERNAL_LIBS) {
73       if(!_gdi32) {
74          _gdi32 = LoadLibraryA("gdi32.dll");
75       }
76    }
77    return _gdi32;
78 }
79 
80 package @property HMODULE user32() nothrow { // getter
81    version(DFL_GET_INTERNAL_LIBS) {
82       if(!_user32) {
83          _user32 = LoadLibraryA("user32.dll");
84       }
85    }
86    return _user32;
87 }
88 
89 package @property HMODULE kernel32() nothrow { // getter
90    version(DFL_GET_INTERNAL_LIBS) {
91       if(!_kernel32) {
92          _kernel32 = LoadLibraryA("kernel32.dll");
93       }
94    }
95    return _kernel32;
96 }
97 
98 
99 private:
100 
101 version(DFL_UNICODE)
102 version = STATIC_UNICODE;
103 
104 
105 public void _utfinit() { // package
106    version(DFL_UNICODE) {
107    }
108    else version(DFL_ANSI) {
109    }
110    else {
111       /+
112       OSVERSIONINFOA osv;
113       osv.dwOSVersionInfoSize = OSVERSIONINFOA.sizeof;
114       if(GetVersionExA(&osv)) {
115          useUnicode = osv.dwPlatformId == VER_PLATFORM_WIN32_NT;
116       }
117       +/
118 
119       _user32 = initInternalLib("user32.dll");
120       _kernel32 = initInternalLib("kernel32.dll");
121       _advapi32 = GetModuleHandleA("advapi32.dll"); // Not guaranteed to be loaded.
122       _gdi32 = initInternalLib("gdi32.dll");
123    }
124 }
125 
126 
127 template _getlen(T) {
128    size_t _getlen(T* tz)
129    in {
130       assert(tz);
131    }
132    body {
133       T* p;
134       for(p = tz; *p; p++) {
135       }
136       return p - tz;
137    }
138 }
139 
140 
141 public:
142 
143 Dstringz unsafeStringz(Dstring s) nothrow {
144    if(!s.length) {
145       return "";
146    }
147 
148    // Check if already null terminated.
149    if(!s.ptr[s.length]) { // Disables bounds checking.
150       return s.ptr;
151    }
152 
153    // Need to duplicate with null terminator.
154    char[] result;
155    result = new char[s.length + 1];
156    result[0 .. s.length] = s[];
157    result[s.length] = 0;
158    //return result.ptr;
159    return cast(Dstringz)result.ptr; // Needed in D2.
160 }
161 
162 
163 Dstring unicodeToAnsi(Dwstringz unicode, size_t ulen) {
164    if(!ulen) {
165       return null;
166    }
167 
168    wchar* wsz;
169    char[] result;
170    int len;
171 
172    len = WideCharToMultiByte(0, 0, unicode, ulen, null, 0, null, null);
173    assert(len > 0);
174 
175    result = new char[len];
176    len = WideCharToMultiByte(0, 0, unicode, ulen, result.ptr, len, null, null);
177    assert(len == result.length);
178    //return result[0 .. len - 1];
179    return cast(Dstring)result[0 .. len - 1]; // Needed in D2.
180 }
181 
182 
183 Dwstring ansiToUnicode(Dstringz ansi, size_t len) {
184    wchar[] ws;
185 
186    len++;
187    ws = new wchar[len];
188 
189    len = MultiByteToWideChar(0, 0, ansi, len, ws.ptr, len);
190    //assert(len == ws.length);
191    ws = ws[0 .. len - 1]; // Exclude null char at end.
192 
193    //return ws;
194    return cast(Dwstring)ws; // Needed in D2.
195 }
196 
197 
198 Dstring fromAnsi(Dstringz ansi, size_t len) {
199    return utf16stringtoUtf8string(ansiToUnicode(ansi, len));
200 }
201 
202 version(DFL_D2_AND_ABOVE) {
203    Dstring fromAnsi(char* ansi, size_t len) {
204       return fromAnsi(cast(Dstringz)ansi, len);
205    }
206 }
207 
208 
209 Dstring fromAnsiz(Dstringz ansiz) {
210    if(!ansiz) {
211       return null;
212    }
213 
214    //return fromAnsi(ansiz, _getlen!(char)(ansiz));
215    return fromAnsi(ansiz, _getlen(ansiz));
216 }
217 
218 version(DFL_D2_AND_ABOVE) {
219    Dstring fromAnsiz(char* ansi) {
220       return fromAnsiz(cast(Dstringz)ansi);
221    }
222 }
223 
224 
225 private Dstring _toAnsiz(Dstring utf8, bool safe = true) {
226    // This function is intentionally unsafe; depends on "safe" param.
227    foreach(char ch; utf8) {
228       if(ch >= 0x80) {
229          char[] result;
230          auto wsz = utf8stringToUtf16stringz(utf8);
231          auto len = WideCharToMultiByte(0, 0, wsz, -1, null, 0, null, null);
232          assert(len > 0);
233 
234          result = new char[len];
235          len = WideCharToMultiByte(0, 0, wsz, -1, result.ptr, len, null, null);
236          assert(len == result.length);
237          //return result[0 .. len - 1];
238          return cast(Dstring)result[0 .. len - 1]; // Needed in D2.
239       }
240    }
241 
242    // Don't need conversion.
243    if(safe)
244       //return stringToStringz(utf8)[0 .. utf8.length];
245    {
246       return cast(Dstring)stringToStringz(utf8)[0 .. utf8.length];   // Needed in D2.
247    }
248    return unsafeStringz(utf8)[0 .. utf8.length];
249 }
250 
251 
252 private size_t toAnsiLength(Dstring utf8) {
253    foreach(char ch; utf8) {
254       if(ch >= 0x80) {
255          auto wsz = utf8stringToUtf16stringz(utf8);
256          auto len = WideCharToMultiByte(0, 0, wsz, -1, null, 0, null, null);
257          assert(len > 0);
258          return len - 1; // Minus null.
259       }
260    }
261    return utf8.length; // Just ASCII; same length.
262 }
263 
264 
265 private Dstring _unsafeAnsiz(Dstring utf8) {
266    return _toAnsiz(utf8, false);
267 }
268 
269 
270 Dstringz toAnsiz(Dstring utf8, bool safe = true) {
271    return _toAnsiz(utf8, safe).ptr;
272 }
273 
274 
275 Dstringz unsafeAnsiz(Dstring utf8) {
276    return _toAnsiz(utf8, false).ptr;
277 }
278 
279 
280 Dstring toAnsi(Dstring utf8, bool safe = true) {
281    return _toAnsiz(utf8, safe);
282 }
283 
284 
285 Dstring unsafeAnsi(Dstring utf8) {
286    return _toAnsiz(utf8, false);
287 }
288 
289 
290 Dstring fromUnicode(Dwstringz unicode, size_t len) {
291    return utf16stringtoUtf8string(unicode[0 .. len]);
292 }
293 
294 version(DFL_D2_AND_ABOVE) {
295    Dstring fromUnicode(wchar* unicode, size_t len) {
296       return fromUnicode(cast(Dwstringz)unicode, len);
297    }
298 }
299 
300 
301 Dstring fromUnicodez(Dwstringz unicodez) {
302    if(!unicodez) {
303       return null;
304    }
305 
306    //return fromUnicode(unicodez, _getlen!(wchar)(unicodez));
307    return fromUnicode(unicodez, _getlen(unicodez));
308 }
309 
310 version(DFL_D2_AND_ABOVE) {
311    Dstring fromUnicodez(wchar* unicodez) {
312       return fromUnicodez(cast(Dwstringz)unicodez);
313    }
314 }
315 
316 
317 Dwstringz toUnicodez(Dstring utf8) {
318    //return utf8stringToUtf16stringz(utf8);
319    return cast(Dwstringz)utf8stringToUtf16stringz(utf8); // Needed in D2.
320 }
321 
322 
323 Dwstring toUnicode(Dstring utf8) {
324    return utf8stringtoUtf16string(utf8);
325 }
326 
327 
328 size_t toUnicodeLength(Dstring utf8) {
329    size_t result = 0;
330    foreach(wchar wch; utf8) {
331       result++;
332    }
333    return result;
334 }
335 
336 
337 extern(Windows) {
338    alias HWND function(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle,
339                        int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
340                        LPVOID lpParam) CreateWindowExWProc;
341    alias int function(HWND hWnd) GetWindowTextLengthWProc;
342    alias int function(HWND hWnd, LPCWSTR lpString, int nMaxCount) GetWindowTextWProc;
343    alias BOOL function(HWND hWnd, LPCWSTR lpString) SetWindowTextWProc;
344    alias LRESULT function(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) SendMessageWProc;
345    alias LRESULT function(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
346    CallWindowProcWProc;
347    alias UINT function(LPCWSTR lpszFormat) RegisterClipboardFormatWProc;
348    alias int function (UINT format, LPWSTR lpszFormatName, int cchMaxCount)
349    GetClipboardFormatNameWProc;
350    alias int function(HDC hdc, LPWSTR lpchText, int cchText, LPRECT lprc, UINT dwDTFormat,
351                       LPDRAWTEXTPARAMS lpDTParams) DrawTextExWProc;
352    alias BOOL function(LPCWSTR lpPathName) SetCurrentDirectoryWProc;
353    alias DWORD function(DWORD nBufferLength, LPWSTR lpBuffer) GetCurrentDirectoryWProc;
354    alias BOOL function(LPWSTR lpBuffer, LPDWORD nSize) GetComputerNameWProc;
355    alias UINT function(LPWSTR lpBuffer, UINT uSize) GetSystemDirectoryWProc;
356    alias BOOL function(LPWSTR lpBuffer, LPDWORD nSize) GetUserNameWProc;
357    alias DWORD function(LPCWSTR lpSrc, LPWSTR lpDst, DWORD nSize) ExpandEnvironmentStringsWProc;
358    alias DWORD function(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) GetEnvironmentVariableWProc;
359    alias LONG function(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, BYTE* lpData,
360                        DWORD cbData) RegSetValueExWProc;
361    alias LONG function(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions,
362                        REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult,
363                        LPDWORD lpdwDisposition) RegCreateKeyExWProc;
364    alias LONG function(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired,
365                        PHKEY phkResult) RegOpenKeyExWProc;
366    alias LONG function(HKEY hKey, LPCWSTR lpSubKey) RegDeleteKeyWProc;
367    alias LONG function(HKEY hKey, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcbName, LPDWORD lpReserved,
368                        LPWSTR lpClass, LPDWORD lpcbClass, PFILETIME lpftLastWriteTime) RegEnumKeyExWProc;
369    alias LONG function(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData,
370                        LPDWORD lpcbData) RegQueryValueExWProc;
371    alias LONG function(HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, LPDWORD lpcbValueName,
372                        LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) RegEnumValueWProc;
373    alias ATOM function(WNDCLASSW* lpWndClass) RegisterClassWProc;
374    alias BOOL function(HDC hdc, LPCWSTR lpString, int cbString, LPSIZE lpSize) GetTextExtentPoint32WProc;
375    alias HANDLE function(HINSTANCE hinst, LPCWSTR lpszName, UINT uType, int cxDesired, int cyDesired, UINT fuLoad)
376    LoadImageWProc;
377    alias UINT function(HDROP hDrop, UINT iFile, LPWSTR lpszFile, UINT cch) DragQueryFileWProc;
378    alias DWORD function(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) GetModuleFileNameWProc;
379    alias LONG function(MSG* lpmsg) DispatchMessageWProc;
380    alias BOOL function(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
381    PeekMessageWProc;
382    alias BOOL function(HWND hDlg, LPMSG lpMsg) IsDialogMessageWProc;
383    alias LRESULT function(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) DefWindowProcWProc;
384    alias LRESULT function(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) DefDlgProcWProc;
385    alias LRESULT function(HWND hWnd, HWND hWndMDIClient, UINT uMsg, WPARAM wParam, LPARAM lParam) DefFrameProcWProc;
386    alias LRESULT function(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) DefMDIChildProcWProc;
387    alias BOOL function(HINSTANCE hInstance, LPCWSTR lpClassName, LPWNDCLASSW lpWndClass) GetClassInfoWProc;
388    alias HANDLE function(LPCWSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter) FindFirstChangeNotificationWProc;
389    alias DWORD function(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart) GetFullPathNameWProc;
390    alias typeof(&LoadLibraryExW) LoadLibraryExWProc;
391    alias typeof(&SetMenuItemInfoW) SetMenuItemInfoWProc;
392    alias typeof(&InsertMenuItemW) InsertMenuItemWProc;
393    alias typeof(&CreateFontIndirectW) CreateFontIndirectWProc;
394    package alias typeof(&GetObjectW) GetObjectWProc;
395 }
396 
397 
398 private void getProcErr(Dstring procName) {
399    Dstring errdesc;
400    version(DFL_NO_PROC_ERROR_INFO) {
401    }
402    else {
403       auto le = cast(int)GetLastError();
404       if(le) {
405          errdesc = " (error " ~ intToString(le) ~ ")";
406       }
407    }
408    throw new Exception("Unable to load procedure " ~ procName ~ errdesc);
409 }
410 
411 
412 // If loading from a resource just use LoadImageA().
413 HANDLE loadImage(HINSTANCE hinst, Dstring name, UINT uType, int cxDesired, int cyDesired, UINT fuLoa) {
414    if(useUnicode) {
415       version(STATIC_UNICODE) {
416          alias LoadImageW proc;
417       }
418       else {
419          enum NAME = "LoadImageW";
420          static LoadImageWProc proc = null;
421 
422          if(!proc) {
423             proc = cast(LoadImageWProc)GetProcAddress(user32, NAME.ptr);
424             if(!proc) {
425                getProcErr(NAME);
426             }
427          }
428       }
429 
430       return proc(hinst, toUnicodez(name), uType, cxDesired, cyDesired, fuLoa);
431    } else {
432       return LoadImageA(hinst, unsafeAnsiz(name), uType, cxDesired, cyDesired, fuLoa);
433    }
434 }
435 
436 
437 HWND createWindowEx(DWORD dwExStyle, Dstring className, Dstring windowName, DWORD dwStyle,
438                     int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
439                     LPVOID lpParam) {
440    if(useUnicode) {
441       version(STATIC_UNICODE) {
442          alias CreateWindowExW proc;
443       }
444       else {
445          enum NAME = "CreateWindowExW";
446          static CreateWindowExWProc proc = null;
447 
448          if(!proc) {
449             proc = cast(CreateWindowExWProc)GetProcAddress(user32, NAME.ptr);
450             if(!proc) {
451                getProcErr(NAME);
452             }
453          }
454       }
455 
456       //if(windowName.length)
457       // MessageBoxW(null, toUnicodez(windowName), toUnicodez(className ~ " caption"), 0);
458       return proc(dwExStyle, toUnicodez(className), toUnicodez(windowName), dwStyle,
459                   x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
460    } else {
461       return CreateWindowExA(dwExStyle, unsafeAnsiz(className), unsafeAnsiz(windowName), dwStyle,
462                              x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
463    }
464 }
465 
466 
467 HWND createWindow(Dstring className, Dstring windowName, DWORD dwStyle, int x, int y,
468                   int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HANDLE hInstance, LPVOID lpParam) {
469    return createWindowEx(0, className, windowName, dwStyle, x, y,
470                          nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
471 }
472 
473 
474 Dstring getWindowText(HWND hwnd) {
475    if(useUnicode) {
476       version(STATIC_UNICODE) {
477          alias GetWindowTextW proc;
478          alias GetWindowTextLengthW proclen;
479       }
480       else {
481          enum NAME = "GetWindowTextW";
482          static GetWindowTextWProc proc = null;
483 
484          enum NAMELEN = "GetWindowTextLengthW";
485          static GetWindowTextLengthWProc proclen = null;
486 
487          if(!proc) {
488             proc = cast(GetWindowTextWProc)GetProcAddress(user32, NAME.ptr);
489             if(!proc) {
490                getProcErr(NAME);
491             }
492 
493             //if(!proclen)
494             {
495                proclen = cast(GetWindowTextLengthWProc)GetProcAddress(user32, NAMELEN.ptr);
496                //if(!proclen)
497                // getProcErr(NAMELEN);
498             }
499          }
500       }
501 
502       wchar* buf;
503       size_t len;
504 
505       len = proclen(hwnd);
506       if(!len) {
507          return null;
508       }
509       len++;
510       buf = (new wchar[len]).ptr;
511 
512       len = proc(hwnd, buf, len);
513       return fromUnicode(buf, len);
514    } else {
515       char* buf;
516       size_t len;
517 
518       len = GetWindowTextLengthA(hwnd);
519       if(!len) {
520          return null;
521       }
522       len++;
523       buf = (new char[len]).ptr;
524 
525       len = GetWindowTextA(hwnd, buf, len);
526       return fromAnsi(buf, len);
527    }
528 }
529 
530 
531 BOOL setWindowText(HWND hwnd, Dstring str) {
532    if(useUnicode) {
533       version(STATIC_UNICODE) {
534          alias SetWindowTextW proc;
535       }
536       else {
537          enum NAME = "SetWindowTextW";
538          static SetWindowTextWProc proc = null;
539 
540          if(!proc) {
541             proc = cast(SetWindowTextWProc)GetProcAddress(user32, NAME.ptr);
542             if(!proc) {
543                getProcErr(NAME);
544             }
545          }
546       }
547 
548       return proc(hwnd, toUnicodez(str));
549    } else {
550       return SetWindowTextA(hwnd, unsafeAnsiz(str));
551    }
552 }
553 
554 
555 Dstring getModuleFileName(HMODULE hmod) {
556    if(useUnicode) {
557       version(STATIC_UNICODE) {
558          alias GetModuleFileNameW proc;
559       }
560       else {
561          enum NAME = "GetModuleFileNameW";
562          static GetModuleFileNameWProc proc = null;
563 
564          if(!proc) {
565             proc = cast(GetModuleFileNameWProc)GetProcAddress(kernel32, NAME.ptr);
566             if(!proc) {
567                getProcErr(NAME);
568             }
569          }
570       }
571 
572       wchar[] s;
573       DWORD len;
574       s = new wchar[MAX_PATH];
575       len = proc(hmod, s.ptr, s.length);
576       return fromUnicode(s.ptr, len);
577    } else {
578       char[] s;
579       DWORD len;
580       s = new char[MAX_PATH];
581       len = GetModuleFileNameA(hmod, s.ptr, s.length);
582       return fromAnsi(s.ptr, len);
583    }
584 }
585 
586 
587 version = STATIC_UNICODE_SEND_MESSAGE;
588 
589 
590 version(STATIC_UNICODE_SEND_MESSAGE) {
591 }
592 else {
593    version(DFL_UNICODE) {
594       version = STATIC_UNICODE_SEND_MESSAGE;
595    }
596    else version(DFL_ANSI) {
597    }
598    else {
599       private SendMessageWProc _loadSendMessageW() {
600          enum NAME = "SendMessageW";
601          static SendMessageWProc proc = null;
602 
603          if(!proc) {
604             proc = cast(SendMessageWProc)GetProcAddress(user32, NAME.ptr);
605             if(!proc) {
606                getProcErr(NAME);
607             }
608          }
609 
610          return proc;
611       }
612    }
613 }
614 
615 
616 // Sends EM_GETSELTEXT to a rich text box and returns the text.
617 Dstring emGetSelText(HWND hwnd, size_t selTextLength) {
618    if(useUnicode) {
619       version(STATIC_UNICODE_SEND_MESSAGE) {
620          alias SendMessageW proc;
621       }
622       else {
623          SendMessageWProc proc;
624          proc = _loadSendMessageW();
625       }
626 
627       wchar[] buf;
628       size_t len;
629       buf = new wchar[selTextLength + 1];
630       len = proc(hwnd, EM_GETSELTEXT, 0, cast(LPARAM)buf.ptr);
631       return fromUnicode(buf.ptr, len);
632    } else {
633       char[] buf;
634       size_t len;
635       buf = new char[selTextLength + 1];
636       len = SendMessageA(hwnd, EM_GETSELTEXT, 0, cast(LPARAM)buf.ptr);
637       return fromAnsi(buf.ptr, len);
638    }
639 }
640 
641 
642 // Gets the selected text of an edit box.
643 // This needs to retrieve the entire text and strip out the extra.
644 Dstring getSelectedText(HWND hwnd) {
645    uint v1, v2;
646    uint len;
647 
648    if(useUnicode) {
649       version(STATIC_UNICODE_SEND_MESSAGE) {
650          alias SendMessageW proc;
651       }
652       else {
653          SendMessageWProc proc;
654          proc = _loadSendMessageW();
655       }
656 
657       proc(hwnd, EM_GETSEL, cast(WPARAM)&v1, cast(LPARAM)&v2);
658       if(v1 == v2) {
659          return null;
660       }
661       assert(v2 > v1);
662 
663       len = proc(hwnd, WM_GETTEXTLENGTH, 0, 0);
664       if(len) {
665          len++;
666          wchar* buf;
667          buf = (new wchar[len]).ptr;
668 
669          len = proc(hwnd, WM_GETTEXT, len, cast(LPARAM)buf);
670          if(len) {
671             wchar[] s;
672             s = buf[v1 .. v2].dup;
673             return fromUnicode(s.ptr, s.length);
674          }
675       }
676    } else {
677       SendMessageA(hwnd, EM_GETSEL, cast(WPARAM)&v1, cast(LPARAM)&v2);
678       if(v1 == v2) {
679          return null;
680       }
681       assert(v2 > v1);
682 
683       len = SendMessageA(hwnd, WM_GETTEXTLENGTH, 0, 0);
684       if(len) {
685          len++;
686          char* buf;
687          buf = (new char[len]).ptr;
688 
689          len = SendMessageA(hwnd, WM_GETTEXT, len, cast(LPARAM)buf);
690          if(len) {
691             char[] s;
692             s = buf[v1 .. v2].dup;
693             return fromAnsi(s.ptr, s.length);
694          }
695       }
696    }
697 
698    return null;
699 }
700 
701 
702 // Sends EM_SETPASSWORDCHAR to an edit box.
703 // TODO: check if correct implementation.
704 void emSetPasswordChar(HWND hwnd, dchar pwc) {
705    if(useUnicode) {
706       version(STATIC_UNICODE_SEND_MESSAGE) {
707          alias SendMessageW proc;
708       }
709       else {
710          SendMessageWProc proc;
711          proc = _loadSendMessageW();
712       }
713 
714       proc(hwnd, EM_SETPASSWORDCHAR, pwc, 0); // ?
715    } else {
716       Dstring chs;
717       Dstring ansichs;
718       chs = utf32stringtoUtf8string((&pwc)[0 .. 1]);
719       ansichs = unsafeAnsi(chs);
720 
721       if(ansichs) {
722          SendMessageA(hwnd, EM_SETPASSWORDCHAR, ansichs[0], 0);   // ?
723       }
724    }
725 }
726 
727 
728 // Sends EM_GETPASSWORDCHAR to an edit box.
729 // TODO: check if correct implementation.
730 dchar emGetPasswordChar(HWND hwnd) {
731    if(useUnicode) {
732       version(STATIC_UNICODE_SEND_MESSAGE) {
733          alias SendMessageW proc;
734       }
735       else {
736          SendMessageWProc proc;
737          proc = _loadSendMessageW();
738       }
739 
740       return cast(dchar)proc(hwnd, EM_GETPASSWORDCHAR, 0, 0); // ?
741    } else {
742       char ansich;
743       Dstring chs;
744       Ddstring dchs;
745       ansich = cast(char)SendMessageA(hwnd, EM_GETPASSWORDCHAR, 0, 0);
746       //chs = fromAnsi((&ansich)[0 .. 1], 1);
747       chs = fromAnsi(&ansich, 1);
748       dchs = utf8stringtoUtf32string(chs);
749       if(dchs.length == 1) {
750          return dchs[0];   // ?
751       }
752       return 0;
753    }
754 }
755 
756 
757 LRESULT sendMessage(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
758    if(useUnicode) {
759       version(STATIC_UNICODE_SEND_MESSAGE) {
760          alias SendMessageW proc;
761       }
762       else {
763          SendMessageWProc proc;
764          proc = _loadSendMessageW();
765       }
766 
767       return proc(hwnd, msg, wparam, lparam);
768    } else {
769       return SendMessageA(hwnd, msg, wparam, lparam);
770    }
771 }
772 
773 
774 LRESULT sendMessage(HWND hwnd, UINT msg, WPARAM wparam, Dstring lparam, bool safe = true) {
775    if(useUnicode) {
776       version(STATIC_UNICODE_SEND_MESSAGE) {
777          alias SendMessageW proc;
778       }
779       else {
780          SendMessageWProc proc;
781          proc = _loadSendMessageW();
782       }
783 
784       return proc(hwnd, msg, wparam, cast(LPARAM)toUnicodez(lparam));
785    } else {
786       return SendMessageA(hwnd, msg, wparam, cast(LPARAM)toAnsiz(lparam, safe)); // Can't assume unsafeAnsiz() is OK here.
787    }
788 }
789 
790 
791 LRESULT sendMessageUnsafe(HWND hwnd, UINT msg, WPARAM wparam, Dstring lparam) {
792    return sendMessage(hwnd, msg, wparam, lparam, false);
793 }
794 
795 
796 version = STATIC_UNICODE_CALL_WINDOW_PROC;
797 
798 
799 LRESULT callWindowProc(WNDPROC lpPrevWndFunc, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
800    if(useUnicode) {
801       version(STATIC_UNICODE_CALL_WINDOW_PROC) {
802          alias CallWindowProcW proc;
803       }
804       else {
805          enum NAME = "CallWindowProcW";
806          static CallWindowProcWProc proc = null;
807 
808          if(!proc) {
809             proc = cast(CallWindowProcWProc)GetProcAddress(user32, NAME.ptr);
810             if(!proc) {
811                getProcErr(NAME);
812             }
813          }
814       }
815 
816       return proc(lpPrevWndFunc, hwnd, msg, wparam, lparam);
817    } else {
818       return CallWindowProcA(lpPrevWndFunc, hwnd, msg, wparam, lparam);
819    }
820 }
821 
822 
823 UINT registerClipboardFormat(Dstring formatName) {
824    if(useUnicode) {
825       version(STATIC_UNICODE) {
826          alias RegisterClipboardFormatW proc;
827       }
828       else {
829          enum NAME = "RegisterClipboardFormatW";
830          static RegisterClipboardFormatWProc proc = null;
831 
832          if(!proc) {
833             proc = cast(RegisterClipboardFormatWProc)GetProcAddress(user32, NAME.ptr);
834             if(!proc) {
835                getProcErr(NAME);
836             }
837          }
838       }
839 
840       return proc(toUnicodez(formatName));
841    } else {
842       return RegisterClipboardFormatA(unsafeAnsiz(formatName));
843    }
844 }
845 
846 
847 Dstring getClipboardFormatName(UINT format) {
848    if(useUnicode) {
849       version(STATIC_UNICODE) {
850          alias GetClipboardFormatNameW proc;
851       }
852       else {
853          enum NAME = "GetClipboardFormatNameW";
854          static GetClipboardFormatNameWProc proc = null;
855 
856          if(!proc) {
857             proc = cast(GetClipboardFormatNameWProc)GetProcAddress(user32, NAME.ptr);
858             if(!proc) {
859                getProcErr(NAME);
860             }
861          }
862       }
863 
864       wchar[] buf;
865       int len;
866       buf = new wchar[64];
867       len = proc(format, buf.ptr, buf.length);
868       if(!len) {
869          return null;
870       }
871       return fromUnicode(buf.ptr, len);
872    } else {
873       char[] buf;
874       int len;
875       buf = new char[64];
876       len = GetClipboardFormatNameA(format, buf.ptr, buf.length);
877       if(!len) {
878          return null;
879       }
880       return fromAnsi(buf.ptr, len);
881    }
882 }
883 
884 
885 // On Windows 9x, the number of characters cannot exceed 8192.
886 int drawTextEx(HDC hdc, Dstring text, LPRECT lprc, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams) {
887    // Note: an older version of MSDN says cchText should be -1 for a null terminated string,
888    // whereas the newer MSDN says 1. Lets just play it safe and use a local null terminated
889    // string when the length is 1 so that it won't continue reading past the 1 character,
890    // reguardless of which MSDN version is correct.
891 
892    if(useUnicode) {
893       version(STATIC_UNICODE) {
894          alias DrawTextExW proc;
895       }
896       else {
897          enum NAME = "DrawTextExW";
898          static DrawTextExWProc proc = null;
899 
900          if(!proc) {
901             proc = cast(DrawTextExWProc)GetProcAddress(user32, NAME.ptr);
902             if(!proc) {
903                getProcErr(NAME);
904             }
905          }
906       }
907 
908       /+
909       wchar* strz;
910       strz = toUnicodez(text);
911       return proc(hdc, strz, -1, lprc, dwDTFormat, lpDTParams);
912       +/
913       Dwstring str;
914       wchar[2] tempStr;
915       str = toUnicode(text);
916       if(str.length == 1) {
917          tempStr[0] = str[0];
918          tempStr[1] = 0;
919          //str = tempStr[0 .. 1];
920          str = cast(Dwstring)tempStr[0 .. 1]; // Needed in D2.
921       }
922       //return proc(hdc, str.ptr, str.length, lprc, dwDTFormat, lpDTParams);
923       return proc(hdc, cast(wchar*)str.ptr, str.length, lprc, dwDTFormat, lpDTParams); // Needed in D2.
924    } else {
925       /+
926       char* strz;
927       strz = unsafeAnsiz(text);
928       return DrawTextExA(hdc, strz, -1, lprc, dwDTFormat, lpDTParams);
929       +/
930       Dstring str;
931       char[2] tempStr;
932       str = unsafeAnsi(text);
933       if(str.length == 1) {
934          tempStr[0] = str[0];
935          tempStr[1] = 0;
936          //str = tempStr[0 .. 1];
937          str = cast(Dstring)tempStr[0 .. 1]; // Needed in D2.
938       }
939       //return DrawTextExA(hdc, str.ptr, str.length, lprc, dwDTFormat, lpDTParams);
940       return DrawTextExA(hdc, cast(char*)str.ptr, str.length, lprc, dwDTFormat, lpDTParams); // Needed in D2.
941    }
942 }
943 
944 
945 Dstring getCommandLine() {
946    // Windows 9x supports GetCommandLineW().
947    return dfl.internal.utf.fromUnicodez(GetCommandLineW());
948 }
949 
950 
951 /* MSDN:
952    The current directory state written by the SetCurrentDirectory function
953    is stored as a global variable in each process, therefore multithreaded
954    applications cannot reliably use this value without possible data
955    corruption from other threads that may also be reading or setting this
956    value. This limitation also applies to the GetCurrentDirectory and
957    GetFullPathName functions.
958 */
959 // This doesn't prevent the problem, but it can minimize it.
960 // e.g. file dialogs set it.
961 //class CurDirLockType { }
962 
963 
964 BOOL setCurrentDirectory(Dstring pathName) {
965    //synchronized(typeid(CurDirLockType))
966    {
967       if(useUnicode) {
968          version(STATIC_UNICODE) {
969             alias SetCurrentDirectoryW proc;
970          }
971          else {
972             enum NAME = "SetCurrentDirectoryW";
973             static SetCurrentDirectoryWProc proc = null;
974 
975             if(!proc) {
976                proc = cast(SetCurrentDirectoryWProc)GetProcAddress(kernel32, NAME.ptr);
977                if(!proc) {
978                   getProcErr(NAME);
979                }
980             }
981          }
982 
983          return proc(toUnicodez(pathName));
984       } else {
985          return SetCurrentDirectoryA(unsafeAnsiz(pathName));
986       }
987    }
988 }
989 
990 
991 Dstring getCurrentDirectory() {
992    //synchronized(typeid(CurDirLockType))
993    {
994       if(useUnicode) {
995          version(STATIC_UNICODE) {
996             alias GetCurrentDirectoryW proc;
997          }
998          else {
999             enum NAME = "GetCurrentDirectoryW";
1000             static GetCurrentDirectoryWProc proc = null;
1001 
1002             if(!proc) {
1003                proc = cast(GetCurrentDirectoryWProc)GetProcAddress(kernel32, NAME.ptr);
1004                if(!proc) {
1005                   getProcErr(NAME);
1006                }
1007             }
1008          }
1009 
1010          wchar* buf;
1011          int len;
1012          len = proc(0, null);
1013          buf = (new wchar[len]).ptr;
1014          len = proc(len, buf);
1015          if(!len) {
1016             return null;
1017          }
1018          return fromUnicode(buf, len);
1019       } else {
1020          char* buf;
1021          int len;
1022          len = GetCurrentDirectoryA(0, null);
1023          buf = (new char[len]).ptr;
1024          len = GetCurrentDirectoryA(len, buf);
1025          if(!len) {
1026             return null;
1027          }
1028          return fromAnsi(buf, len);
1029       }
1030    }
1031 }
1032 
1033 
1034 Dstring getFullPathName(Dstring fileName) {
1035    //synchronized(typeid(CurDirLockType))
1036    {
1037       DWORD len;
1038 
1039       if(useUnicode) {
1040          version(STATIC_UNICODE) {
1041             alias GetFullPathNameW proc;
1042          }
1043          else {
1044             enum NAME = "GetFullPathNameW";
1045             static GetFullPathNameWProc proc = null;
1046 
1047             if(!proc) {
1048                proc = cast(GetFullPathNameWProc)GetProcAddress(kernel32, NAME.ptr);
1049                if(!proc) {
1050                   getProcErr(NAME);
1051                }
1052             }
1053          }
1054 
1055          auto fnw = toUnicodez(fileName);
1056          len = proc(fnw, 0, null, null);
1057          if(!len) {
1058             return null;
1059          }
1060          wchar[260] _wbuf;
1061          wchar[] wbuf = _wbuf;
1062          if(len > _wbuf.sizeof) {
1063             wbuf = new wchar[len];
1064          }
1065          len = proc(fnw, wbuf.length, wbuf.ptr, null);
1066          assert(len < wbuf.length);
1067          return fromUnicode(wbuf.ptr, len);
1068       } else {
1069          auto fna = unsafeAnsiz(fileName);
1070          len = GetFullPathNameA(fna, 0, null, null);
1071          if(!len) {
1072             return null;
1073          }
1074          char[260] _abuf;
1075          char[] abuf = _abuf;
1076          if(len > _abuf.sizeof) {
1077             abuf = new char[len];
1078          }
1079          len = GetFullPathNameA(fna, abuf.length, abuf.ptr, null);
1080          assert(len < abuf.length);
1081          return fromAnsi(abuf.ptr, len);
1082       }
1083    }
1084 }
1085 
1086 
1087 Dstring getComputerName() {
1088    if(useUnicode) {
1089       version(STATIC_UNICODE) {
1090          alias GetComputerNameW proc;
1091       }
1092       else {
1093          enum NAME = "GetComputerNameW";
1094          static GetComputerNameWProc proc = null;
1095 
1096          if(!proc) {
1097             proc = cast(GetComputerNameWProc)GetProcAddress(kernel32, NAME.ptr);
1098             if(!proc) {
1099                getProcErr(NAME);
1100             }
1101          }
1102       }
1103 
1104       wchar[] buf;
1105       DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
1106       buf = new wchar[len];
1107       if(!proc(buf.ptr, &len)) {
1108          return null;
1109       }
1110       return fromUnicode(buf.ptr, len);
1111    } else {
1112       char[] buf;
1113       DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
1114       buf = new char[len];
1115       if(!GetComputerNameA(buf.ptr, &len)) {
1116          return null;
1117       }
1118       return fromAnsi(buf.ptr, len);
1119    }
1120 }
1121 
1122 
1123 Dstring getSystemDirectory() {
1124    if(useUnicode) {
1125       version(STATIC_UNICODE) {
1126          alias GetSystemDirectoryW proc;
1127       }
1128       else {
1129          enum NAME = "GetSystemDirectoryW";
1130          static GetSystemDirectoryWProc proc = null;
1131 
1132          if(!proc) {
1133             proc = cast(GetSystemDirectoryWProc)GetProcAddress(kernel32, NAME.ptr);
1134             if(!proc) {
1135                getProcErr(NAME);
1136             }
1137          }
1138       }
1139 
1140       wchar[] buf;
1141       UINT len;
1142       buf = new wchar[MAX_PATH];
1143       len = proc(buf.ptr, buf.length);
1144       if(!len) {
1145          return null;
1146       }
1147       return fromUnicode(buf.ptr, len);
1148    } else {
1149       char[] buf;
1150       UINT len;
1151       buf = new char[MAX_PATH];
1152       len = GetSystemDirectoryA(buf.ptr, buf.length);
1153       if(!len) {
1154          return null;
1155       }
1156       return fromAnsi(buf.ptr, len);
1157    }
1158 }
1159 
1160 
1161 Dstring getUserName() {
1162    if(useUnicode) {
1163       version(STATIC_UNICODE) {
1164          alias GetUserNameW proc;
1165       }
1166       else {
1167          enum NAME = "GetUserNameW";
1168          static GetUserNameWProc proc = null;
1169 
1170          if(!proc) {
1171             proc = cast(GetUserNameWProc)GetProcAddress(advapi32, NAME.ptr);
1172             if(!proc) {
1173                getProcErr(NAME);
1174             }
1175          }
1176       }
1177 
1178       wchar[256 + 1] buf;
1179       DWORD len = buf.length;
1180       if(!proc(buf.ptr, &len) || !len || !--len) { // Also remove null-terminator.
1181          return null;
1182       }
1183       return fromUnicode(buf.ptr, len);
1184    } else {
1185       char[256 + 1] buf;
1186       DWORD len = buf.length;
1187       if(!GetUserNameA(buf.ptr, &len) || !len || !--len) { // Also remove null-terminator.
1188          return null;
1189       }
1190       return fromAnsi(buf.ptr, len);
1191    }
1192 }
1193 
1194 
1195 // Returns 0 on failure.
1196 DWORD expandEnvironmentStrings(Dstring src, out Dstring result) {
1197    if(useUnicode) {
1198       version(STATIC_UNICODE) {
1199          alias ExpandEnvironmentStringsW proc;
1200       }
1201       else {
1202          enum NAME = "ExpandEnvironmentStringsW";
1203          static ExpandEnvironmentStringsWProc proc = null;
1204 
1205          if(!proc) {
1206             proc = cast(ExpandEnvironmentStringsWProc)GetProcAddress(kernel32, NAME.ptr);
1207             if(!proc) {
1208                getProcErr(NAME);
1209             }
1210          }
1211       }
1212 
1213       wchar* dest;
1214       DWORD len;
1215 
1216       auto strz = toUnicodez(src);
1217       len = proc(strz, null, 0);
1218       if(!len) {
1219          return 0;
1220       }
1221       dest = (new wchar[len]).ptr;
1222       len = proc(strz, dest, len);
1223       if(!len) {
1224          return 0;
1225       }
1226       result = fromUnicode(dest, len - 1);
1227       return len;
1228    } else {
1229       char* dest;
1230       DWORD len;
1231 
1232       auto strz = unsafeAnsiz(src);
1233       len = ExpandEnvironmentStringsA(strz, null, 0);
1234       if(!len) {
1235          return 0;
1236       }
1237       dest = (new char[len]).ptr;
1238       len = ExpandEnvironmentStringsA(strz, dest, len);
1239       if(!len) {
1240          return 0;
1241       }
1242       result = fromAnsi(dest, len - 1);
1243       return len;
1244    }
1245 }
1246 
1247 
1248 Dstring getEnvironmentVariable(Dstring name) {
1249    if(useUnicode) {
1250       version(STATIC_UNICODE) {
1251          alias GetEnvironmentVariableW proc;
1252       }
1253       else {
1254          enum NAME = "GetEnvironmentVariableW";
1255          static GetEnvironmentVariableWProc proc = null;
1256 
1257          if(!proc) {
1258             proc = cast(GetEnvironmentVariableWProc)GetProcAddress(kernel32, NAME.ptr);
1259             if(!proc) {
1260                getProcErr(NAME);
1261             }
1262          }
1263       }
1264 
1265       wchar* buf;
1266       DWORD len;
1267       auto strz = toUnicodez(name);
1268       len = proc(strz, null, 0);
1269       if(!len) {
1270          return null;
1271       }
1272       buf = (new wchar[len]).ptr;
1273       len = proc(strz, buf, len);
1274       return fromUnicode(buf, len);
1275    } else {
1276       char* buf;
1277       DWORD len;
1278       auto strz = unsafeAnsiz(name);
1279       len = GetEnvironmentVariableA(strz, null, 0);
1280       if(!len) {
1281          return null;
1282       }
1283       buf = (new char[len]).ptr;
1284       len = GetEnvironmentVariableA(strz, buf, len);
1285       return fromAnsi(buf, len);
1286    }
1287 }
1288 
1289 
1290 int messageBox(HWND hWnd, Dstring text, Dstring caption, UINT uType) {
1291    // Windows 9x supports MessageBoxW().
1292    return MessageBoxW(hWnd, toUnicodez(text), toUnicodez(caption), uType);
1293 }
1294 
1295 
1296 struct WndClass {
1297    union {
1298       WNDCLASSW wcw;
1299       WNDCLASSA wca;
1300    }
1301    alias wcw wc;
1302 
1303    Dstring className;
1304 }
1305 
1306 
1307 ATOM registerClass(ref WndClass wc) {
1308    if(useUnicode) {
1309       version(STATIC_UNICODE) {
1310          alias RegisterClassW proc;
1311       }
1312       else {
1313          enum NAME = "RegisterClassW";
1314          static RegisterClassWProc proc = null;
1315 
1316          if(!proc) {
1317             proc = cast(RegisterClassWProc)GetProcAddress(user32, NAME.ptr);
1318             if(!proc) {
1319                getProcErr(NAME);
1320             }
1321          }
1322       }
1323 
1324       wc.wcw.lpszClassName = toUnicodez(wc.className);
1325       return proc(&wc.wcw);
1326    } else {
1327       wc.wca.lpszClassName = unsafeAnsiz(wc.className);
1328       return RegisterClassA(&wc.wca);
1329    }
1330 }
1331 
1332 
1333 BOOL getClassInfo(HINSTANCE hinst, Dstring className, ref WndClass wc) {
1334    wc.className = className; // ?
1335 
1336    if(useUnicode) {
1337       version(STATIC_UNICODE) {
1338          alias GetClassInfoW proc;
1339       }
1340       else {
1341          enum NAME = "GetClassInfoW";
1342          static GetClassInfoWProc proc = null;
1343 
1344          if(!proc) {
1345             proc = cast(GetClassInfoWProc)GetProcAddress(user32, NAME.ptr);
1346             if(!proc) {
1347                getProcErr(NAME);
1348             }
1349          }
1350       }
1351 
1352       return proc(hinst, toUnicodez(className), &wc.wcw);
1353    } else {
1354       return GetClassInfoA(hinst, unsafeAnsiz(className), &wc.wca);
1355    }
1356 }
1357 
1358 
1359 // Shouldn't have been implemented this way.
1360 deprecated BOOL getTextExtentPoint32(HDC hdc, Dstring text, LPSIZE lpSize) {
1361    if(useUnicode) {
1362       version(STATIC_UNICODE) {
1363          alias GetTextExtentPoint32W proc;
1364       }
1365       else {
1366          enum NAME = "GetTextExtentPoint32W";
1367          static GetTextExtentPoint32WProc proc = null;
1368 
1369          if(!proc) {
1370             proc = cast(GetTextExtentPoint32WProc)GetProcAddress(gdi32, NAME.ptr);
1371             if(!proc) {
1372                getProcErr(NAME);
1373             }
1374          }
1375       }
1376 
1377       Dwstring str;
1378       str = toUnicode(text);
1379       return proc(hdc, str.ptr, str.length, lpSize);
1380    } else {
1381       // Using GetTextExtentPoint32A here even though W is supported in order
1382       // to keep the measurements accurate with DrawTextA.
1383       Dstring str;
1384       str = unsafeAnsi(text);
1385       return GetTextExtentPoint32A(hdc, str.ptr, str.length, lpSize);
1386    }
1387 }
1388 
1389 
1390 Dstring dragQueryFile(HDROP hDrop, UINT iFile) {
1391    if(iFile >= 0xFFFFFFFF) {
1392       return null;
1393    }
1394 
1395    if(useUnicode) {
1396       version(STATIC_UNICODE) {
1397          alias DragQueryFileW proc;
1398       }
1399       else {
1400          enum NAME = "DragQueryFileW";
1401          static DragQueryFileWProc proc = null;
1402 
1403          if(!proc) {
1404             proc = cast(DragQueryFileWProc)GetProcAddress(GetModuleHandleA("shell32.dll"), NAME.ptr);
1405             if(!proc) {
1406                getProcErr(NAME);
1407             }
1408          }
1409       }
1410 
1411       wchar[] str;
1412       UINT len;
1413       len = proc(hDrop, iFile, null, 0);
1414       if(!len) {
1415          return null;
1416       }
1417       str = new wchar[len + 1];
1418       proc(hDrop, iFile, str.ptr, str.length);
1419       return fromUnicode(str.ptr, len);
1420    } else {
1421       char[] str;
1422       UINT len;
1423       len = DragQueryFileA(hDrop, iFile, null, 0);
1424       if(!len) {
1425          return null;
1426       }
1427       str = new char[len + 1];
1428       DragQueryFileA(hDrop, iFile, str.ptr, str.length);
1429       return fromAnsi(str.ptr, len);
1430    }
1431 }
1432 
1433 
1434 // Just gets the number of files.
1435 UINT dragQueryFile(HDROP hDrop) {
1436    return DragQueryFileA(hDrop, 0xFFFFFFFF, null, 0);
1437 }
1438 
1439 
1440 HANDLE createFile(Dstring fileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
1441                   DWORD dwCreationDistribution, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
1442    if(useUnicode) {
1443       return CreateFileW(toUnicodez(fileName), dwDesiredAccess, dwShareMode, lpSecurityAttributes,
1444                          dwCreationDistribution, dwFlagsAndAttributes, hTemplateFile);
1445    } else {
1446       return CreateFileA(unsafeAnsiz(fileName), dwDesiredAccess, dwShareMode, lpSecurityAttributes,
1447                          dwCreationDistribution, dwFlagsAndAttributes, hTemplateFile);
1448    }
1449 }
1450 
1451 
1452 version = STATIC_UNICODE_DEF_WINDOW_PROC;
1453 
1454 
1455 LRESULT defWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1456    if(useUnicode) {
1457       version(STATIC_UNICODE_DEF_WINDOW_PROC) {
1458          alias DefWindowProcW proc;
1459       }
1460       else {
1461          enum NAME = "DefWindowProcW";
1462          static DefWindowProcWProc proc = null;
1463 
1464          if(!proc) {
1465             proc = cast(DefWindowProcWProc)GetProcAddress(user32, NAME.ptr);
1466             if(!proc) {
1467                getProcErr(NAME);
1468             }
1469          }
1470       }
1471 
1472       return proc(hwnd, msg, wparam, lparam);
1473    } else {
1474       return DefWindowProcA(hwnd, msg, wparam, lparam);
1475    }
1476 }
1477 
1478 
1479 LRESULT defDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1480    if(useUnicode) {
1481       version(STATIC_UNICODE_DEF_WINDOW_PROC) {
1482          alias DefDlgProcW proc;
1483       }
1484       else {
1485          enum NAME = "DefDlgProcW";
1486          static DefDlgProcWProc proc = null;
1487 
1488          if(!proc) {
1489             proc = cast(DefDlgProcWProc)GetProcAddress(user32, NAME.ptr);
1490             if(!proc) {
1491                getProcErr(NAME);
1492             }
1493          }
1494       }
1495 
1496       return proc(hwnd, msg, wparam, lparam);
1497    } else {
1498       return DefDlgProcA(hwnd, msg, wparam, lparam);
1499    }
1500 }
1501 
1502 
1503 LRESULT defFrameProc(HWND hwnd, HWND hwndMdiClient, UINT msg, WPARAM wparam, LPARAM lparam) {
1504    if(useUnicode) {
1505       version(STATIC_UNICODE_DEF_WINDOW_PROC) {
1506          alias DefFrameProcW proc;
1507       }
1508       else {
1509          enum NAME = "DefFrameProcW";
1510          static DefFrameProcWProc proc = null;
1511 
1512          if(!proc) {
1513             proc = cast(DefFrameProcWProc)GetProcAddress(user32, NAME.ptr);
1514             if(!proc) {
1515                getProcErr(NAME);
1516             }
1517          }
1518       }
1519 
1520       return proc(hwnd, hwndMdiClient, msg, wparam, lparam);
1521    } else {
1522       return DefFrameProcA(hwnd, hwndMdiClient, msg, wparam, lparam);
1523    }
1524 }
1525 
1526 
1527 LRESULT defMDIChildProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1528    if(useUnicode) {
1529       version(STATIC_UNICODE_DEF_WINDOW_PROC) {
1530          alias DefMDIChildProcW proc;
1531       }
1532       else {
1533          enum NAME = "DefMDIChildProcW";
1534          static DefMDIChildProcWProc proc = null;
1535 
1536          if(!proc) {
1537             proc = cast(DefMDIChildProcWProc)GetProcAddress(user32, NAME.ptr);
1538             if(!proc) {
1539                getProcErr(NAME);
1540             }
1541          }
1542       }
1543 
1544       return proc(hwnd, msg, wparam, lparam);
1545    } else {
1546       return DefMDIChildProcA(hwnd, msg, wparam, lparam);
1547    }
1548 }
1549 
1550 
1551 version = STATIC_UNICODE_PEEK_MESSAGE;
1552 version = STATIC_UNICODE_DISPATCH_MESSAGE;
1553 
1554 
1555 LONG dispatchMessage(MSG* pmsg) {
1556    if(useUnicode) {
1557       version(STATIC_UNICODE_DISPATCH_MESSAGE) {
1558          alias DispatchMessageW dispatchproc;
1559       }
1560       else {
1561          enum DISPATCHNAME = "DispatchMessageW";
1562          static DispatchMessageWProc dispatchproc = null;
1563 
1564          if(!dispatchproc) {
1565             dispatchproc = cast(DispatchMessageWProc)GetProcAddress(user32, DISPATCHNAME);
1566             if(!dispatchproc) {
1567                getProcErr(DISPATCHNAME);
1568             }
1569          }
1570       }
1571 
1572       return dispatchproc(pmsg);
1573    } else {
1574       return DispatchMessageA(pmsg);
1575    }
1576 }
1577 
1578 
1579 BOOL peekMessage(MSG* pmsg, HWND hwnd = HWND.init, UINT wmFilterMin = 0, UINT wmFilterMax = 0, UINT removeMsg = PM_NOREMOVE) {
1580    if(useUnicode) {
1581       version(STATIC_UNICODE_PEEK_MESSAGE) {
1582          alias PeekMessageW peekproc;
1583       }
1584       else {
1585          enum PEEKNAME = "PeekMessageW";
1586          static PeekMessageWProc peekproc = null;
1587 
1588          if(!peekproc) {
1589             peekproc = cast(PeekMessageWProc)GetProcAddress(user32, PEEKNAME);
1590             if(!peekproc) {
1591                getProcErr(PEEKNAME);
1592             }
1593          }
1594       }
1595 
1596       /+
1597       // Using PeekMessageA to test if the window is unicod.
1598       if(!PeekMessageA(pmsg, hwnd, wmFilterMin, wmFilterMax, PM_NOREMOVE)) { // Don't remove to test if unicode.
1599          return 0;
1600       }
1601       if(!IsWindowUnicode(pmsg.hwnd)) { // Window is not unicode.
1602          if(removeMsg == PM_NOREMOVE) {
1603             return 1;   // No need to do extra work here.
1604          }
1605          return PeekMessageA(pmsg, hwnd, wmFilterMin, wmFilterMax, removeMsg);
1606       } else { // Window is unicode.
1607          return peekproc(pmsg, hwnd, wmFilterMin, wmFilterMax, removeMsg);
1608       }
1609       +/
1610       // Since I already know useUnicode, use PeekMessageW to test if the window is unicode.
1611       if(!peekproc(pmsg, hwnd, wmFilterMin, wmFilterMax, PM_NOREMOVE)) { // Don't remove to test if unicode.
1612          return 0;
1613       }
1614       if(!IsWindowUnicode(pmsg.hwnd)) { // Window is not unicode.
1615          return PeekMessageA(pmsg, hwnd, wmFilterMin, wmFilterMax, removeMsg);
1616       } else { // Window is unicode.
1617          if(removeMsg == PM_NOREMOVE) {
1618             return 1;   // No need to do extra work here.
1619          }
1620          return peekproc(pmsg, hwnd, wmFilterMin, wmFilterMax, removeMsg);
1621       }
1622    } else {
1623       return PeekMessageA(pmsg, hwnd, wmFilterMin, wmFilterMax, removeMsg);
1624    }
1625 }
1626 
1627 
1628 BOOL getMessage(MSG* pmsg, HWND hwnd = HWND.init, UINT wmFilterMin = 0, UINT wmFilterMax = 0) {
1629    if(!WaitMessage()) {
1630       return -1;
1631    }
1632    if(!peekMessage(pmsg, hwnd, wmFilterMin, wmFilterMax, PM_REMOVE)) {
1633       return -1;
1634    }
1635    if(WM_QUIT == pmsg.message) {
1636       return 0;
1637    }
1638    return 1;
1639 }
1640 
1641 
1642 BOOL isDialogMessage(HWND hwnd, MSG* pmsg) {
1643    if(useUnicode) {
1644       version(STATIC_UNICODE) {
1645          alias IsDialogMessageW proc;
1646       }
1647       else {
1648          enum NAME = "IsDialogMessageW";
1649          static IsDialogMessageWProc proc = null;
1650 
1651          if(!proc) {
1652             proc = cast(IsDialogMessageWProc)GetProcAddress(user32, NAME.ptr);
1653             if(!proc) {
1654                getProcErr(NAME);
1655             }
1656          }
1657       }
1658 
1659       return proc(hwnd, pmsg);
1660    } else {
1661       return IsDialogMessageA(hwnd, pmsg);
1662    }
1663 }
1664 
1665 
1666 HANDLE findFirstChangeNotification(Dstring pathName, BOOL watchSubtree, DWORD notifyFilter) {
1667    if(useUnicode) {
1668       version(STATIC_UNICODE) {
1669          alias FindFirstChangeNotificationW proc;
1670       }
1671       else {
1672          enum NAME = "FindFirstChangeNotificationW";
1673          static FindFirstChangeNotificationWProc proc = null;
1674 
1675          if(!proc) {
1676             proc = cast(FindFirstChangeNotificationWProc)GetProcAddress(kernel32, NAME.ptr);
1677             if(!proc) {
1678                getProcErr(NAME);
1679             }
1680          }
1681       }
1682 
1683       return proc(toUnicodez(pathName), watchSubtree, notifyFilter);
1684    } else {
1685       return FindFirstChangeNotificationA(unsafeAnsiz(pathName), watchSubtree, notifyFilter);
1686    }
1687 }
1688 
1689 
1690 HINSTANCE loadLibraryEx(Dstring libFileName, DWORD flags) {
1691    if(useUnicode) {
1692       version(STATIC_UNICODE) {
1693          alias LoadLibraryExW proc;
1694       }
1695       else {
1696          enum NAME = "LoadLibraryExW";
1697          static LoadLibraryExWProc proc = null;
1698 
1699          if(!proc) {
1700             proc = cast(LoadLibraryExWProc)GetProcAddress(kernel32, NAME.ptr);
1701             if(!proc) {
1702                getProcErr(NAME);
1703             }
1704          }
1705       }
1706 
1707       return proc(toUnicodez(libFileName), HANDLE.init, flags);
1708    } else {
1709       return LoadLibraryExA(unsafeAnsiz(libFileName), HANDLE.init, flags);
1710    }
1711 }
1712 
1713 
1714 BOOL _setMenuItemInfoW(HMENU hMenu, UINT uItem, BOOL fByPosition, LPMENUITEMINFOW lpmii) { // package
1715    if(useUnicode) {
1716       version(STATIC_UNICODE) {
1717          alias SetMenuItemInfoW proc;
1718       }
1719       else {
1720          enum NAME = "SetMenuItemInfoW";
1721          static SetMenuItemInfoWProc proc = null;
1722 
1723          if(!proc) {
1724             proc = cast(SetMenuItemInfoWProc)GetProcAddress(user32, NAME.ptr);
1725             if(!proc) {
1726                getProcErr(NAME);
1727             }
1728          }
1729       }
1730 
1731       return proc(hMenu, uItem, fByPosition, lpmii);
1732    } else {
1733       assert(0);
1734    }
1735 }
1736 
1737 
1738 BOOL _insertMenuItemW(HMENU hMenu, UINT uItem, BOOL fByPosition, LPMENUITEMINFOW lpmii) { // package
1739    if(useUnicode) {
1740       version(STATIC_UNICODE) {
1741          alias InsertMenuItemW proc;
1742       }
1743       else {
1744          enum NAME = "InsertMenuItemW";
1745          static InsertMenuItemWProc proc = null;
1746 
1747          if(!proc) {
1748             proc = cast(InsertMenuItemWProc)GetProcAddress(user32, NAME.ptr);
1749             if(!proc) {
1750                getProcErr(NAME);
1751             }
1752          }
1753       }
1754 
1755       return proc(hMenu, uItem, fByPosition, lpmii);
1756    } else {
1757       assert(0);
1758    }
1759 }
1760 
1761 
1762 Dstring regQueryValueString(HKEY hkey, Dstring valueName, LPDWORD lpType = null) {
1763    DWORD _type;
1764    if(!lpType) {
1765       lpType = &_type;
1766    }
1767 
1768    DWORD sz;
1769 
1770    if(useUnicode) {
1771       version(STATIC_UNICODE) {
1772          alias RegQueryValueExW proc;
1773       }
1774       else {
1775          enum NAME = "RegQueryValueExW";
1776          static RegQueryValueExWProc proc = null;
1777 
1778          if(!proc) {
1779             proc = cast(RegQueryValueExWProc)GetProcAddress(advapi32, NAME.ptr);
1780             if(!proc) {
1781                getProcErr(NAME);
1782             }
1783          }
1784       }
1785 
1786       //sz = 0;
1787       auto lpValueName = toUnicodez(valueName);
1788       proc(hkey, lpValueName, null, lpType, null, &sz);
1789       if(!sz || (REG_SZ != *lpType && REG_EXPAND_SZ != *lpType)) {
1790          return null;
1791       }
1792       wchar[] ws = new wchar[sz];
1793       if(ERROR_SUCCESS != proc(hkey, lpValueName, null, null, cast(LPBYTE)ws.ptr, &sz)) {
1794          return null;
1795       }
1796       //return fromUnicode(ws.ptr, ws.length - 1); // Somehow ends up throwing invalid UTF-16.
1797       return fromUnicodez(ws.ptr);
1798    } else {
1799       //sz = 0;
1800       auto lpValueName = toAnsiz(valueName);
1801       RegQueryValueExA(hkey, lpValueName, null, lpType, null, &sz);
1802       if(!sz || (REG_SZ != *lpType && REG_EXPAND_SZ != *lpType)) {
1803          return null;
1804       }
1805       char[] s = new char[sz];
1806       if(ERROR_SUCCESS != RegQueryValueExA(hkey, lpValueName, null, null, cast(LPBYTE)s.ptr, &sz)) {
1807          return null;
1808       }
1809       //return fromAnsi(s.ptr, s.length - 1);
1810       return fromAnsiz(s.ptr);
1811    }
1812 }
1813 
1814 
1815 struct LogFont {
1816    union {
1817       LOGFONTW lfw;
1818       LOGFONTA lfa;
1819    }
1820    alias lfw lf;
1821 
1822    Dstring faceName;
1823 }
1824 
1825 
1826 HFONT createFontIndirect(ref LogFont lf) {
1827    if(useUnicode) {
1828       version(STATIC_UNICODE) {
1829          alias CreateFontIndirectW proc;
1830       }
1831       else {
1832          enum NAME = "CreateFontIndirectW";
1833          static CreateFontIndirectWProc proc = null;
1834 
1835          if(!proc) {
1836             proc = cast(CreateFontIndirectWProc)GetProcAddress(gdi32, NAME.ptr);
1837             if(!proc) {
1838                getProcErr(NAME);
1839             }
1840          }
1841       }
1842 
1843       Dwstring ws = toUnicode(lf.faceName);
1844       if(ws.length >= LF_FACESIZE) {
1845          ws = ws[0 .. LF_FACESIZE - 1];   // ?
1846       }
1847       foreach(idx, wch; ws) {
1848          lf.lfw.lfFaceName[idx] = wch;
1849       }
1850       lf.lfw.lfFaceName[ws.length] = 0;
1851 
1852       return proc(&lf.lfw);
1853    } else {
1854       Dstring as = toAnsi(lf.faceName);
1855       if(as.length >= LF_FACESIZE) {
1856          as = as[0 .. LF_FACESIZE - 1];   // ?
1857       }
1858       foreach(idx, ach; as) {
1859          lf.lfa.lfFaceName[idx] = ach;
1860       }
1861       lf.lfa.lfFaceName[as.length] = 0;
1862 
1863       return CreateFontIndirectA(&lf.lfa);
1864    }
1865 }
1866 
1867 
1868 // GetObject for a LogFont.
1869 int getLogFont(HFONT hf, ref LogFont lf) {
1870    if(useUnicode) {
1871       version(STATIC_UNICODE) {
1872          alias GetObjectW proc;
1873       }
1874       else {
1875          enum NAME = "GetObjectW";
1876          static GetObjectWProc proc = null;
1877 
1878          if(!proc) {
1879             proc = cast(GetObjectWProc)GetProcAddress(gdi32, NAME.ptr);
1880             if(!proc) {
1881                getProcErr(NAME);
1882             }
1883          }
1884       }
1885 
1886       if(LOGFONTW.sizeof != proc(hf, LOGFONTW.sizeof, &lf.lfw)) {
1887          return 0;
1888       }
1889       lf.faceName = fromUnicodez(lf.lfw.lfFaceName.ptr);
1890       return LOGFONTW.sizeof;
1891    } else {
1892       if(LOGFONTA.sizeof != GetObjectA(hf, LOGFONTA.sizeof, &lf.lfa)) {
1893          return 0;
1894       }
1895       lf.faceName = fromAnsiz(lf.lfa.lfFaceName.ptr);
1896       return LOGFONTA.sizeof;
1897    }
1898 }
1899