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 73 string sysErrorString( 74 DWORD errCode, 75 // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language 76 int langId = LANG_NEUTRAL, 77 int subLangId = SUBLANG_DEFAULT) @trusted 78 { 79 auto buf = appender!string(); 80 81 wenforce( 82 // Ignore unlikely UTF decoding errors, always report the actual error (`errCode`) 83 putSysError(errCode, buf, MAKELANGID(langId, subLangId)).ifThrown(false), 84 text("Could not fetch error string for WinAPI code ", errCode) 85 ); 86 87 return buf.data; 88 } 89 90 @safe unittest 91 { 92 import std.algorithm.searching; 93 94 assert(sysErrorString(ERROR_PATH_NOT_FOUND) !is null); 95 96 const msg = collectExceptionMsg!WindowsException(sysErrorString(DWORD.max)); 97 assert(msg.startsWith(`Could not fetch error string for WinAPI code 4294967295: `)); 98 } 99 100 bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) 101 { 102 wchar *lpMsgBuf = null; 103 auto res = FormatMessageW( 104 FORMAT_MESSAGE_ALLOCATE_BUFFER | 105 FORMAT_MESSAGE_FROM_SYSTEM | 106 FORMAT_MESSAGE_IGNORE_INSERTS, 107 null, 108 code, 109 langId, 110 cast(LPWSTR)&lpMsgBuf, 111 0, 112 null); 113 scope(exit) if (lpMsgBuf) LocalFree(lpMsgBuf); 114 115 if (lpMsgBuf) 116 { 117 import std.string : strip; 118 w.put(lpMsgBuf[0 .. res].strip()); 119 return true; 120 } 121 else 122 return false; 123 } 124 125 class WindowsException : Exception 126 { 127 import core.sys.windows.windef : DWORD; 128 129 final @property DWORD code() { return _code; } /// `GetLastError`'s return value. 130 private DWORD _code; 131 132 this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted 133 { 134 _code = code; 135 136 auto buf = appender!(char[]); 137 138 if (str != null) 139 { 140 buf.put(str); 141 if (code) 142 buf.put(": "); 143 } 144 145 if (code && writeErrorMessage(code, buf)) 146 { 147 buf.put(" (error "); 148 toTextRange(code, buf); 149 buf.put(')'); 150 } 151 152 super(cast(immutable) buf.data, file, line); 153 } 154 } 155 156 /// Writes the error string associated to `code` into `buf`. 157 /// Writes `Error <code>` when the error message lookup fails 158 private bool writeErrorMessage(DWORD code, ref Appender!(char[]) buf) nothrow 159 { 160 bool success; 161 try 162 { 163 // Reset the buffer to undo partial changes 164 const len = buf[].length; 165 scope (failure) buf.shrinkTo(len); 166 167 success = putSysError(code, buf); 168 } 169 catch (Exception) {} 170 171 // Write the error code instead if we couldn't find the string 172 if (!success) 173 { 174 buf.put("Error "); 175 toTextRange(code, buf); 176 } 177 178 return success; 179 } 180 181 T wenforce(T, S = string)(T value, lazy S msg = null, 182 string file = __FILE__, size_t line = __LINE__) 183 if (isSomeString!S) 184 { 185 if (!value) 186 throw new WindowsException(GetLastError(), to!string(msg), file, line); 187 return value; 188 } 189 190 T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file = __FILE__, size_t line = __LINE__) 191 { 192 if (condition) 193 return condition; 194 string names; 195 if (!name) 196 { 197 static string trustedToString(const(wchar)* stringz) @trusted 198 { 199 import core.stdc.wchar_ : wcslen; 200 import std.conv : to; 201 auto len = wcslen(stringz); 202 return to!string(stringz[0 .. len]); 203 } 204 205 names = trustedToString(namez); 206 } 207 else 208 names = to!string(name); 209 throw new WindowsException(GetLastError(), names, file, line); 210 } 211 212 @system unittest 213 { 214 import std.algorithm.searching : startsWith, endsWith; 215 import std.string; 216 217 auto e = collectException!WindowsException( 218 DeleteFileA("unexisting.txt").wenforce("DeleteFile") 219 ); 220 assert(e.code == ERROR_FILE_NOT_FOUND); 221 assert(e.msg.startsWith("DeleteFile: ")); 222 // can't test the entire message, as it depends on Windows locale 223 assert(e.msg.endsWith(" (error 2)")); 224 225 // Test code zero 226 e = new WindowsException(0); 227 assert(e.msg == ""); 228 229 e = new WindowsException(0, "Test"); 230 assert(e.msg == "Test"); 231 } 232 233 @safe nothrow unittest 234 { 235 import std.algorithm.searching : endsWith; 236 237 auto e = new WindowsException(ERROR_FILE_NOT_FOUND); 238 assert(e.msg.endsWith("(error 2)")); 239 240 e = new WindowsException(DWORD.max); 241 assert(e.msg == "Error 4294967295"); 242 } 243 244 /// Tries to translate an error code from the Windows API to the corresponding 245 /// error message. Returns `Error <code>` on failure 246 package (std) string generateSysErrorMsg(DWORD errCode = GetLastError()) nothrow @trusted 247 { 248 auto buf = appender!(char[]); 249 cast(void) writeErrorMessage(errCode, buf); 250 return cast(immutable) buf[]; 251 } 252 253 nothrow @safe unittest 254 { 255 assert(generateSysErrorMsg(ERROR_PATH_NOT_FOUND) !is null); 256 assert(generateSysErrorMsg(DWORD.max) == "Error 4294967295"); 257 }