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