1 // Written in the D programming language. 2 3 /** 4 * Convert Win32 error code to string. 5 * 6 * Copyright: Copyright The D Language Foundation" 2006 - 2013. 7 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 8 * Authors: $(HTTP digitalmars.com, Walter Bright) 9 * Credits: Based on code written by Regan Heath 10 */ 11 /* Copyright The D Language Foundation" 2006 - 2013. 12 * Distributed under the Boost Software License, Version 1.0. 13 * (See accompanying file LICENSE_1_0.txt or copy at 14 * http://www.boost.org/LICENSE_1_0.txt) 15 */ 16 module std.windows.syserror; 17 import std.traits : isSomeString; 18 19 version (StdDdoc) 20 { 21 private 22 { 23 alias DWORD = uint; 24 enum LANG_NEUTRAL = 0, SUBLANG_DEFAULT = 1; 25 } 26 27 /** Query the text for a Windows error code, as returned by 28 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, 29 `GetLastError`), as a D string. 30 */ 31 string sysErrorString( 32 DWORD errCode, 33 // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language 34 int langId = LANG_NEUTRAL, 35 int subLangId = SUBLANG_DEFAULT) @trusted; 36 37 /********************* 38 Thrown if errors that set 39 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, 40 `GetLastError`) occur. 41 */ 42 class WindowsException : Exception 43 { 44 private alias DWORD = int; 45 final @property DWORD code(); /// `GetLastError`'s return value. 46 this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted; 47 } 48 49 /++ 50 If `!!value` is true, `value` is returned. Otherwise, 51 $(D new WindowsException(GetLastError(), msg)) is thrown. 52 `WindowsException` assumes that the last operation set 53 `GetLastError()` appropriately. 54 55 Example: 56 -------------------- 57 wenforce(DeleteFileA("junk.tmp"), "DeleteFile failed"); 58 -------------------- 59 +/ 60 T wenforce(T, S)(T value, lazy S msg = null, 61 string file = __FILE__, size_t line = __LINE__) @safe 62 if (isSomeString!S); 63 } 64 else: 65 66 version (Windows): 67 68 import core.sys.windows.winbase, core.sys.windows.winnt; 69 import std.array : appender, Appender; 70 import std.conv : to, toTextRange, text; 71 import std.exception; 72 import std.windows.charset; 73 74 string sysErrorString( 75 DWORD errCode, 76 // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language 77 int langId = LANG_NEUTRAL, 78 int subLangId = SUBLANG_DEFAULT) @trusted 79 { 80 auto buf = appender!string(); 81 82 wenforce( 83 // Ignore unlikely UTF decoding errors, always report the actual error (`errCode`) 84 putSysError(errCode, buf, MAKELANGID(langId, subLangId)).ifThrown(false), 85 text("Could not fetch error string for WinAPI code ", errCode) 86 ); 87 88 return buf.data; 89 } 90 91 @safe unittest 92 { 93 import std.algorithm.searching; 94 95 assert(sysErrorString(ERROR_PATH_NOT_FOUND) !is null); 96 97 const msg = collectExceptionMsg!WindowsException(sysErrorString(DWORD.max)); 98 assert(msg.startsWith(`Could not fetch error string for WinAPI code 4294967295: `)); 99 } 100 101 bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) 102 { 103 wchar *lpMsgBuf = null; 104 auto res = FormatMessageW( 105 FORMAT_MESSAGE_ALLOCATE_BUFFER | 106 FORMAT_MESSAGE_FROM_SYSTEM | 107 FORMAT_MESSAGE_IGNORE_INSERTS, 108 null, 109 code, 110 langId, 111 cast(LPWSTR)&lpMsgBuf, 112 0, 113 null); 114 scope(exit) if (lpMsgBuf) LocalFree(lpMsgBuf); 115 116 if (lpMsgBuf) 117 { 118 import std.string : strip; 119 w.put(lpMsgBuf[0 .. res].strip()); 120 return true; 121 } 122 else 123 return false; 124 } 125 126 class WindowsException : Exception 127 { 128 import core.sys.windows.windef : DWORD; 129 130 final @property DWORD code() { return _code; } /// `GetLastError`'s return value. 131 private DWORD _code; 132 133 this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted 134 { 135 _code = code; 136 137 auto buf = appender!(char[]); 138 139 if (str != null) 140 { 141 buf.put(str); 142 if (code) 143 buf.put(": "); 144 } 145 146 if (code && writeErrorMessage(code, buf)) 147 { 148 buf.put(" (error "); 149 toTextRange(code, buf); 150 buf.put(')'); 151 } 152 153 super(cast(immutable) buf.data, file, line); 154 } 155 } 156 157 /// Writes the error string associated to `code` into `buf`. 158 /// Writes `Error <code>` when the error message lookup fails 159 private bool writeErrorMessage(DWORD code, ref Appender!(char[]) buf) nothrow 160 { 161 bool success; 162 try 163 { 164 // Reset the buffer to undo partial changes 165 const len = buf[].length; 166 scope (failure) buf.shrinkTo(len); 167 168 success = putSysError(code, buf); 169 } 170 catch (Exception) {} 171 172 // Write the error code instead if we couldn't find the string 173 if (!success) 174 { 175 buf.put("Error "); 176 toTextRange(code, buf); 177 } 178 179 return success; 180 } 181 182 T wenforce(T, S = string)(T value, lazy S msg = null, 183 string file = __FILE__, size_t line = __LINE__) 184 if (isSomeString!S) 185 { 186 if (!value) 187 throw new WindowsException(GetLastError(), to!string(msg), file, line); 188 return value; 189 } 190 191 T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file = __FILE__, size_t line = __LINE__) 192 { 193 if (condition) 194 return condition; 195 string names; 196 if (!name) 197 { 198 static string trustedToString(const(wchar)* stringz) @trusted 199 { 200 import core.stdc.wchar_ : wcslen; 201 import std.conv : to; 202 auto len = wcslen(stringz); 203 return to!string(stringz[0 .. len]); 204 } 205 206 names = trustedToString(namez); 207 } 208 else 209 names = to!string(name); 210 throw new WindowsException(GetLastError(), names, file, line); 211 } 212 213 @system unittest 214 { 215 import std.algorithm.searching : startsWith, endsWith; 216 import std.string; 217 218 auto e = collectException!WindowsException( 219 DeleteFileA("unexisting.txt").wenforce("DeleteFile") 220 ); 221 assert(e.code == ERROR_FILE_NOT_FOUND); 222 assert(e.msg.startsWith("DeleteFile: ")); 223 // can't test the entire message, as it depends on Windows locale 224 assert(e.msg.endsWith(" (error 2)")); 225 226 // Test code zero 227 e = new WindowsException(0); 228 assert(e.msg == ""); 229 230 e = new WindowsException(0, "Test"); 231 assert(e.msg == "Test"); 232 } 233 234 @safe nothrow unittest 235 { 236 import std.algorithm.searching : endsWith; 237 238 auto e = new WindowsException(ERROR_FILE_NOT_FOUND); 239 assert(e.msg.endsWith("(error 2)")); 240 241 e = new WindowsException(DWORD.max); 242 assert(e.msg == "Error 4294967295"); 243 } 244 245 /// Tries to translate an error code from the Windows API to the corresponding 246 /// error message. Returns `Error <code>` on failure 247 package (std) string generateSysErrorMsg(DWORD errCode = GetLastError()) nothrow @trusted 248 { 249 auto buf = appender!(char[]); 250 cast(void) writeErrorMessage(errCode, buf); 251 return cast(immutable) buf[]; 252 } 253 254 nothrow @safe unittest 255 { 256 assert(generateSysErrorMsg(ERROR_PATH_NOT_FOUND) !is null); 257 assert(generateSysErrorMsg(DWORD.max) == "Error 4294967295"); 258 }