1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
27 static const GUID CLSID_ShellBtrfsIconHandler
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf0 } };
28 static const GUID CLSID_ShellBtrfsContextMenu
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf1 } };
29 static const GUID CLSID_ShellBtrfsPropSheet
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf2 } };
30 static const GUID CLSID_ShellBtrfsVolPropSheet
= { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf3 } };
32 #define COM_DESCRIPTION_ICON_HANDLER L"WinBtrfs shell extension (icon handler)"
33 #define COM_DESCRIPTION_CONTEXT_MENU L"WinBtrfs shell extension (context menu)"
34 #define COM_DESCRIPTION_PROP_SHEET L"WinBtrfs shell extension (property sheet)"
35 #define COM_DESCRIPTION_VOL_PROP_SHEET L"WinBtrfs shell extension (volume property sheet)"
36 #define ICON_OVERLAY_NAME L"WinBtrfs"
38 typedef enum _PROCESS_DPI_AWARENESS
{
40 PROCESS_SYSTEM_DPI_AWARE
,
41 PROCESS_PER_MONITOR_DPI_AWARE
42 } PROCESS_DPI_AWARENESS
;
44 typedef ULONG (WINAPI
*_RtlNtStatusToDosError
)(NTSTATUS Status
);
45 typedef HRESULT (WINAPI
*_SetProcessDpiAwareness
)(PROCESS_DPI_AWARENESS value
);
50 void set_dpi_aware() {
51 _SetProcessDpiAwareness SetProcessDpiAwareness
;
52 HMODULE shcore
= LoadLibraryW(L
"shcore.dll");
57 SetProcessDpiAwareness
= (_SetProcessDpiAwareness
)GetProcAddress(shcore
, "SetProcessDpiAwareness");
59 if (!SetProcessDpiAwareness
)
62 SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE
);
65 void format_size(uint64_t size
, wstring
& s
, bool show_bytes
) {
66 wstring t
, bytes
, kb
, nb
;
71 WCHAR dec
[2], thou
[4], grouping
[64], *c
;
77 nb
= to_wstring(size
);
79 swprintf(buffer
, L
"%I64d", size
);
83 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, thou
, sizeof(thou
) / sizeof(WCHAR
));
85 dec
[0] = '.'; dec
[1] = 0; // not used, but silences gcc warning
89 fmt
.lpDecimalSep
= dec
;
90 fmt
.lpThousandSep
= thou
;
91 fmt
.NegativeOrder
= 0;
93 // Grouping code copied from dlls/shlwapi/string.c in Wine - thank you
96 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SGROUPING
, grouping
, sizeof(grouping
) / sizeof(WCHAR
));
100 if (*c
>= '0' && *c
< '9') {
102 fmt
.Grouping
+= *c
- '0';
108 if (fmt
.Grouping
% 10 == 0)
113 GetNumberFormatW(LOCALE_USER_DEFAULT
, 0, nb
.c_str(), &fmt
, nb2
, sizeof(nb2
) / sizeof(WCHAR
));
116 if (!load_string(module
, size
== 1 ? IDS_SIZE_BYTE
: IDS_SIZE_BYTES
, t
))
117 throw last_error(GetLastError());
119 wstring_sprintf(s
, t
, nb2
);
124 if (!load_string(module
, IDS_SIZE_BYTES
, t
))
125 throw last_error(GetLastError());
127 wstring_sprintf(bytes
, t
, nb2
);
130 if (size
>= 1152921504606846976) {
132 f
= (float)size
/ 1152921504606846976.0f
;
133 } else if (size
>= 1125899906842624) {
135 f
= (float)size
/ 1125899906842624.0f
;
136 } else if (size
>= 1099511627776) {
138 f
= (float)size
/ 1099511627776.0f
;
139 } else if (size
>= 1073741824) {
141 f
= (float)size
/ 1073741824.0f
;
142 } else if (size
>= 1048576) {
144 f
= (float)size
/ 1048576.0f
;
147 f
= (float)size
/ 1024.0f
;
150 if (!load_string(module
, sr
, t
))
151 throw last_error(GetLastError());
154 wstring_sprintf(kb
, t
, f
);
156 if (!load_string(module
, IDS_SIZE_LARGE
, t
))
157 throw last_error(GetLastError());
159 wstring_sprintf(s
, t
, kb
.c_str(), bytes
.c_str());
161 wstring_sprintf(s
, t
, f
);
164 wstring
format_message(ULONG last_error
) {
168 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, nullptr,
169 last_error
, 0, (WCHAR
*)&buf
, 0, nullptr) == 0) {
170 return L
"(error retrieving message)";
177 // remove trailing newline
178 while (s
.length() > 0 && (s
.substr(s
.length() - 1, 1) == L
"\r" || s
.substr(s
.length() - 1, 1) == L
"\n"))
179 s
= s
.substr(0, s
.length() - 1);
184 wstring
format_ntstatus(NTSTATUS Status
) {
185 _RtlNtStatusToDosError RtlNtStatusToDosError
;
187 HMODULE ntdll
= LoadLibraryW(L
"ntdll.dll");
190 return L
"(error loading ntdll.dll)";
192 RtlNtStatusToDosError
= (_RtlNtStatusToDosError
)GetProcAddress(ntdll
, "RtlNtStatusToDosError");
194 if (!RtlNtStatusToDosError
) {
196 return L
"(error loading RtlNtStatusToDosError)";
199 s
= format_message(RtlNtStatusToDosError(Status
));
206 bool load_string(HMODULE module
, UINT id
, wstring
& s
) {
208 LPWSTR retstr
= nullptr;
210 len
= LoadStringW(module
, id
, (LPWSTR
)&retstr
, 0);
215 s
= wstring(retstr
, len
);
221 #pragma warning(push)
222 #pragma warning(disable: 4996)
225 void wstring_sprintf(wstring
& s
, wstring fmt
, ...) {
230 len
= _vsnwprintf(nullptr, 0, fmt
.c_str(), args
);
236 _vsnwprintf((wchar_t*)s
.c_str(), len
, fmt
.c_str(), args
);
246 extern "C" STDAPI
DllCanUnloadNow(void) {
247 return objs_loaded
== 0 ? S_OK
: S_FALSE
;
250 extern "C" STDAPI
DllGetClassObject(REFCLSID rclsid
, REFIID riid
, LPVOID
* ppv
) {
251 if (rclsid
== CLSID_ShellBtrfsIconHandler
) {
252 Factory
* fact
= new Factory
;
254 return E_OUTOFMEMORY
;
256 fact
->type
= FactoryIconHandler
;
258 return fact
->QueryInterface(riid
, ppv
);
260 } else if (rclsid
== CLSID_ShellBtrfsContextMenu
) {
261 Factory
* fact
= new Factory
;
263 return E_OUTOFMEMORY
;
265 fact
->type
= FactoryContextMenu
;
267 return fact
->QueryInterface(riid
, ppv
);
269 } else if (rclsid
== CLSID_ShellBtrfsPropSheet
) {
270 Factory
* fact
= new Factory
;
272 return E_OUTOFMEMORY
;
274 fact
->type
= FactoryPropSheet
;
276 return fact
->QueryInterface(riid
, ppv
);
278 } else if (rclsid
== CLSID_ShellBtrfsVolPropSheet
) {
279 Factory
* fact
= new Factory
;
281 return E_OUTOFMEMORY
;
283 fact
->type
= FactoryVolPropSheet
;
285 return fact
->QueryInterface(riid
, ppv
);
289 return CLASS_E_CLASSNOTAVAILABLE
;
292 static void write_reg_key(HKEY root
, const wstring
& keyname
, const WCHAR
* val
, const wstring
& data
) {
297 l
= RegCreateKeyExW(root
, keyname
.c_str(), 0, nullptr, 0, KEY_ALL_ACCESS
, nullptr, &hk
, &dispos
);
298 if (l
!= ERROR_SUCCESS
)
299 throw string_error(IDS_REGCREATEKEY_FAILED
, l
);
301 l
= RegSetValueExW(hk
, val
, 0, REG_SZ
, (const BYTE
*)data
.c_str(), (data
.length() + 1) * sizeof(WCHAR
));
302 if (l
!= ERROR_SUCCESS
)
303 throw string_error(IDS_REGSETVALUEEX_FAILED
, l
);
306 if (l
!= ERROR_SUCCESS
)
307 throw string_error(IDS_REGCLOSEKEY_FAILED
, l
);
310 static void register_clsid(const GUID clsid
, const WCHAR
* description
) {
312 wstring inproc
, progid
, clsidkeyname
;
313 WCHAR dllpath
[MAX_PATH
];
315 StringFromCLSID(clsid
, &clsidstring
);
319 inproc
= L
"CLSID\\"s
+ clsidstring
+ L
"\\InprocServer32"s
;
320 progid
= L
"CLSID\\"s
+ clsidstring
+ L
"\\ProgId"s
;
321 clsidkeyname
= L
"CLSID\\"s
+ clsidstring
;
323 inproc
= wstring(L
"CLSID\\") + clsidstring
+ wstring(L
"\\InprocServer32");
324 progid
= wstring(L
"CLSID\\") + clsidstring
+ wstring(L
"\\ProgId");
325 clsidkeyname
= wstring(L
"CLSID\\") + clsidstring
;
328 write_reg_key(HKEY_CLASSES_ROOT
, clsidkeyname
, nullptr, description
);
330 GetModuleFileNameW(module
, dllpath
, sizeof(dllpath
));
332 write_reg_key(HKEY_CLASSES_ROOT
, inproc
, nullptr, dllpath
);
334 write_reg_key(HKEY_CLASSES_ROOT
, inproc
, L
"ThreadingModel", L
"Apartment");
336 CoTaskMemFree(clsidstring
);
340 CoTaskMemFree(clsidstring
);
343 static void unregister_clsid(const GUID clsid
) {
346 StringFromCLSID(clsid
, &clsidstring
);
349 WCHAR clsidkeyname
[MAX_PATH
];
351 wsprintfW(clsidkeyname
, L
"CLSID\\%s", clsidstring
);
353 LONG l
= RegDeleteTreeW(HKEY_CLASSES_ROOT
, clsidkeyname
);
355 if (l
!= ERROR_SUCCESS
)
356 throw string_error(IDS_REGDELETETREE_FAILED
, l
);
358 CoTaskMemFree(clsidstring
);
362 CoTaskMemFree(clsidstring
);
365 static void reg_icon_overlay(const GUID clsid
, const wstring
& name
) {
368 StringFromCLSID(clsid
, &clsidstring
);
372 wstring path
= L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\"s
+ name
;
374 wstring path
= wstring(L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\") + name
;
377 write_reg_key(HKEY_LOCAL_MACHINE
, path
, nullptr, clsidstring
);
379 CoTaskMemFree(clsidstring
);
383 CoTaskMemFree(clsidstring
);
386 static void unreg_icon_overlay(const wstring
& name
) {
388 wstring path
= L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\"s
+ name
;
390 wstring path
= wstring(L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\") + name
;
393 LONG l
= RegDeleteTreeW(HKEY_LOCAL_MACHINE
, path
.c_str());
395 if (l
!= ERROR_SUCCESS
)
396 throw string_error(IDS_REGDELETETREE_FAILED
, l
);
399 static void reg_context_menu_handler(const GUID clsid
, const wstring
& filetype
, const wstring
& name
) {
402 StringFromCLSID(clsid
, &clsidstring
);
406 wstring path
= filetype
+ L
"\\ShellEx\\ContextMenuHandlers\\"s
+ name
;
408 wstring path
= filetype
+ wstring(L
"\\ShellEx\\ContextMenuHandlers\\") + name
;
411 write_reg_key(HKEY_CLASSES_ROOT
, path
, nullptr, clsidstring
);
413 CoTaskMemFree(clsidstring
);
418 static void unreg_context_menu_handler(const wstring
& filetype
, const wstring
& name
) {
420 wstring path
= filetype
+ L
"\\ShellEx\\ContextMenuHandlers\\"s
+ name
;
422 wstring path
= filetype
+ wstring(L
"\\ShellEx\\ContextMenuHandlers\\") + name
;
425 LONG l
= RegDeleteTreeW(HKEY_CLASSES_ROOT
, path
.c_str());
427 if (l
!= ERROR_SUCCESS
)
428 throw string_error(IDS_REGDELETETREE_FAILED
, l
);
431 static void reg_prop_sheet_handler(const GUID clsid
, const wstring
& filetype
, const wstring
& name
) {
434 StringFromCLSID(clsid
, &clsidstring
);
438 wstring path
= filetype
+ L
"\\ShellEx\\PropertySheetHandlers\\"s
+ name
;
440 wstring path
= filetype
+ wstring(L
"\\ShellEx\\PropertySheetHandlers\\") + name
;
443 write_reg_key(HKEY_CLASSES_ROOT
, path
, nullptr, clsidstring
);
445 CoTaskMemFree(clsidstring
);
450 static void unreg_prop_sheet_handler(const wstring
& filetype
, const wstring
& name
) {
452 wstring path
= filetype
+ L
"\\ShellEx\\PropertySheetHandlers\\"s
+ name
;
454 wstring path
= filetype
+ wstring(L
"\\ShellEx\\PropertySheetHandlers\\") + name
;
457 LONG l
= RegDeleteTreeW(HKEY_CLASSES_ROOT
, path
.c_str());
459 if (l
!= ERROR_SUCCESS
)
460 throw string_error(IDS_REGDELETETREE_FAILED
, l
);
463 extern "C" STDAPI
DllRegisterServer(void) {
465 register_clsid(CLSID_ShellBtrfsIconHandler
, COM_DESCRIPTION_ICON_HANDLER
);
466 register_clsid(CLSID_ShellBtrfsContextMenu
, COM_DESCRIPTION_CONTEXT_MENU
);
467 register_clsid(CLSID_ShellBtrfsPropSheet
, COM_DESCRIPTION_PROP_SHEET
);
468 register_clsid(CLSID_ShellBtrfsVolPropSheet
, COM_DESCRIPTION_VOL_PROP_SHEET
);
470 reg_icon_overlay(CLSID_ShellBtrfsIconHandler
, ICON_OVERLAY_NAME
);
472 reg_context_menu_handler(CLSID_ShellBtrfsContextMenu
, L
"Directory\\Background", ICON_OVERLAY_NAME
);
473 reg_context_menu_handler(CLSID_ShellBtrfsContextMenu
, L
"Folder", ICON_OVERLAY_NAME
);
475 reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet
, L
"Folder", ICON_OVERLAY_NAME
);
476 reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet
, L
"*", ICON_OVERLAY_NAME
);
477 reg_prop_sheet_handler(CLSID_ShellBtrfsVolPropSheet
, L
"Drive", ICON_OVERLAY_NAME
);
478 } catch (const exception
& e
) {
479 error_message(nullptr, e
.what());
486 extern "C" STDAPI
DllUnregisterServer(void) {
488 unreg_prop_sheet_handler(L
"Folder", ICON_OVERLAY_NAME
);
489 unreg_prop_sheet_handler(L
"*", ICON_OVERLAY_NAME
);
490 unreg_prop_sheet_handler(L
"Drive", ICON_OVERLAY_NAME
);
491 unreg_context_menu_handler(L
"Folder", ICON_OVERLAY_NAME
);
492 unreg_context_menu_handler(L
"Directory\\Background", ICON_OVERLAY_NAME
);
493 unreg_icon_overlay(ICON_OVERLAY_NAME
);
495 unregister_clsid(CLSID_ShellBtrfsVolPropSheet
);
496 unregister_clsid(CLSID_ShellBtrfsPropSheet
);
497 unregister_clsid(CLSID_ShellBtrfsContextMenu
);
498 unregister_clsid(CLSID_ShellBtrfsIconHandler
);
499 } catch (const exception
& e
) {
500 error_message(nullptr, e
.what());
507 extern "C" STDAPI
DllInstall(BOOL bInstall
, LPCWSTR pszCmdLine
) {
509 return DllRegisterServer();
511 return DllUnregisterServer();
514 extern "C" BOOL APIENTRY
DllMain(HANDLE hModule
, DWORD dwReason
, void* lpReserved
) {
515 if (dwReason
== DLL_PROCESS_ATTACH
)
516 module
= (HMODULE
)hModule
;
521 static void create_subvol(const wstring
& fn
) {
522 size_t found
= fn
.rfind(L
"\\");
526 btrfs_create_subvol
* bcs
;
527 IO_STATUS_BLOCK iosb
;
529 if (found
== wstring::npos
) {
533 path
= fn
.substr(0, found
);
534 file
= fn
.substr(found
+ 1);
538 h
= CreateFileW(path
.c_str(), FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, nullptr);
540 if (h
== INVALID_HANDLE_VALUE
)
543 bcslen
= offsetof(btrfs_create_subvol
, name
[0]) + (file
.length() * sizeof(WCHAR
));
544 bcs
= (btrfs_create_subvol
*)malloc(bcslen
);
546 bcs
->readonly
= false;
548 bcs
->namelen
= (uint16_t)(file
.length() * sizeof(WCHAR
));
549 memcpy(bcs
->name
, file
.c_str(), bcs
->namelen
);
551 NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_CREATE_SUBVOL
, bcs
, bcslen
, nullptr, 0);
554 extern "C" void CALLBACK
CreateSubvolW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
555 vector
<wstring
> args
;
557 command_line_to_args(lpszCmdLine
, args
);
559 if (args
.size() >= 1)
560 create_subvol(args
[0]);
563 static void create_snapshot2(const wstring
& source
, const wstring
& fn
) {
564 size_t found
= fn
.rfind(L
"\\");
568 btrfs_create_snapshot
* bcs
;
569 IO_STATUS_BLOCK iosb
;
571 if (found
== wstring::npos
) {
575 path
= fn
.substr(0, found
);
576 file
= fn
.substr(found
+ 1);
580 src
= CreateFileW(source
.c_str(), FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, nullptr);
581 if (src
== INVALID_HANDLE_VALUE
)
584 h
= CreateFileW(path
.c_str(), FILE_ADD_SUBDIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, nullptr);
586 if (h
== INVALID_HANDLE_VALUE
)
589 bcslen
= offsetof(btrfs_create_snapshot
, name
[0]) + (file
.length() * sizeof(WCHAR
));
590 bcs
= (btrfs_create_snapshot
*)malloc(bcslen
);
592 bcs
->readonly
= false;
594 bcs
->namelen
= (uint16_t)(file
.length() * sizeof(WCHAR
));
595 memcpy(bcs
->name
, file
.c_str(), bcs
->namelen
);
598 NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_CREATE_SNAPSHOT
, bcs
, bcslen
, nullptr, 0);
601 extern "C" void CALLBACK
CreateSnapshotW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
602 vector
<wstring
> args
;
604 command_line_to_args(lpszCmdLine
, args
);
606 if (args
.size() >= 2)
607 create_snapshot2(args
[0], args
[1]);
610 void command_line_to_args(LPWSTR cmdline
, vector
<wstring
> args
) {
616 l
= CommandLineToArgvW(cmdline
, &num_args
);
622 args
.reserve(num_args
);
624 for (unsigned int i
= 0; i
< (unsigned int)num_args
; i
++) {
625 args
.push_back(l
[i
]);
636 #pragma warning(push)
637 #pragma warning(disable: 4996)
640 string_error::string_error(int resno
, ...) {
645 if (!load_string(module
, resno
, fmt
))
646 throw runtime_error("LoadString failed."); // FIXME
648 va_start(args
, resno
);
649 len
= _vsnwprintf(nullptr, 0, fmt
.c_str(), args
);
655 _vsnwprintf((wchar_t*)s
.c_str(), len
, fmt
.c_str(), args
);
660 utf16_to_utf8(s
, msg
);
667 void utf8_to_utf16(const string
& utf8
, wstring
& utf16
) {
672 Status
= RtlUTF8ToUnicodeN(nullptr, 0, &utf16len
, utf8
.c_str(), utf8
.length());
673 if (!NT_SUCCESS(Status
))
674 throw string_error(IDS_RECV_RTLUTF8TOUNICODEN_FAILED
, Status
, format_ntstatus(Status
).c_str());
676 buf
= (WCHAR
*)malloc(utf16len
+ sizeof(WCHAR
));
679 throw string_error(IDS_OUT_OF_MEMORY
);
681 Status
= RtlUTF8ToUnicodeN(buf
, utf16len
, &utf16len
, utf8
.c_str(), utf8
.length());
682 if (!NT_SUCCESS(Status
)) {
684 throw string_error(IDS_RECV_RTLUTF8TOUNICODEN_FAILED
, Status
, format_ntstatus(Status
).c_str());
687 buf
[utf16len
/ sizeof(WCHAR
)] = 0;
694 void utf16_to_utf8(const wstring
& utf16
, string
& utf8
) {
699 Status
= RtlUnicodeToUTF8N(nullptr, 0, &utf8len
, utf16
.c_str(), utf16
.length() * sizeof(WCHAR
));
700 if (!NT_SUCCESS(Status
))
701 throw string_error(IDS_RECV_RTLUNICODETOUTF8N_FAILED
, Status
, format_ntstatus(Status
).c_str());
703 buf
= (char*)malloc(utf8len
+ sizeof(char));
706 throw string_error(IDS_OUT_OF_MEMORY
);
708 Status
= RtlUnicodeToUTF8N(buf
, utf8len
, &utf8len
, utf16
.c_str(), utf16
.length() * sizeof(WCHAR
));
709 if (!NT_SUCCESS(Status
)) {
711 throw string_error(IDS_RECV_RTLUNICODETOUTF8N_FAILED
, Status
, format_ntstatus(Status
).c_str());
721 last_error::last_error(DWORD errnum
) {
724 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, nullptr,
725 errnum
, 0, (WCHAR
*)&buf
, 0, nullptr) == 0)
726 throw runtime_error("FormatMessage failed");
729 utf16_to_utf8(buf
, msg
);
738 void error_message(HWND hwnd
, const char* msg
) {
741 load_string(module
, IDS_ERROR
, title
);
743 utf8_to_utf16(msg
, wmsg
);
745 MessageBoxW(hwnd
, wmsg
.c_str(), title
.c_str(), MB_ICONERROR
);
748 ntstatus_error::ntstatus_error(NTSTATUS Status
) {
749 _RtlNtStatusToDosError RtlNtStatusToDosError
;
750 HMODULE ntdll
= LoadLibraryW(L
"ntdll.dll");
754 throw runtime_error("Error loading ntdll.dll.");
757 RtlNtStatusToDosError
= (_RtlNtStatusToDosError
)GetProcAddress(ntdll
, "RtlNtStatusToDosError");
759 if (!RtlNtStatusToDosError
)
760 throw runtime_error("Error loading RtlNtStatusToDosError in ntdll.dll.");
762 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, nullptr,
763 RtlNtStatusToDosError(Status
), 0, (WCHAR
*)&buf
, 0, nullptr) == 0)
764 throw runtime_error("FormatMessage failed");
767 utf16_to_utf8(buf
, msg
);
783 NTSTATUS NTAPI
RtlUnicodeToUTF8N(CHAR
*utf8_dest
, ULONG utf8_bytes_max
,
784 ULONG
*utf8_bytes_written
,
785 const WCHAR
*uni_src
, ULONG uni_bytes
)
795 return STATUS_INVALID_PARAMETER_4
;
796 if (!utf8_bytes_written
)
797 return STATUS_INVALID_PARAMETER
;
798 if (utf8_dest
&& uni_bytes
% sizeof(WCHAR
))
799 return STATUS_INVALID_PARAMETER_5
;
802 status
= STATUS_SUCCESS
;
804 for (i
= 0; i
< uni_bytes
/ sizeof(WCHAR
); i
++)
806 /* decode UTF-16 into ch */
808 if (ch
>= 0xdc00 && ch
<= 0xdfff)
811 status
= STATUS_SOME_NOT_MAPPED
;
813 else if (ch
>= 0xd800 && ch
<= 0xdbff)
815 if (i
+ 1 < uni_bytes
/ sizeof(WCHAR
))
819 if (uni_src
[i
+ 1] >= 0xdc00 && uni_src
[i
+ 1] <= 0xdfff)
821 ch
|= uni_src
[i
+ 1] - 0xdc00;
828 status
= STATUS_SOME_NOT_MAPPED
;
834 status
= STATUS_SOME_NOT_MAPPED
;
838 /* encode ch as UTF-8 */
839 ASSERT(ch
<= 0x10ffff);
842 utf8_ch
[0] = ch
& 0x7f;
847 utf8_ch
[0] = 0xc0 | (ch
>> 6 & 0x1f);
848 utf8_ch
[1] = 0x80 | (ch
>> 0 & 0x3f);
851 else if (ch
< 0x10000)
853 utf8_ch
[0] = 0xe0 | (ch
>> 12 & 0x0f);
854 utf8_ch
[1] = 0x80 | (ch
>> 6 & 0x3f);
855 utf8_ch
[2] = 0x80 | (ch
>> 0 & 0x3f);
858 else if (ch
< 0x200000)
860 utf8_ch
[0] = 0xf0 | (ch
>> 18 & 0x07);
861 utf8_ch
[1] = 0x80 | (ch
>> 12 & 0x3f);
862 utf8_ch
[2] = 0x80 | (ch
>> 6 & 0x3f);
863 utf8_ch
[3] = 0x80 | (ch
>> 0 & 0x3f);
869 written
+= utf8_ch_len
;
873 if (utf8_bytes_max
>= utf8_ch_len
)
875 memcpy(utf8_dest
, utf8_ch
, utf8_ch_len
);
876 utf8_dest
+= utf8_ch_len
;
877 utf8_bytes_max
-= utf8_ch_len
;
878 written
+= utf8_ch_len
;
883 status
= STATUS_BUFFER_TOO_SMALL
;
887 *utf8_bytes_written
= written
;