1 // Written in the D programming language.
2
3 /**
4 * Convert Win32 error code to string.
5 *
6 * Copyright: Copyright Digital Mars 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 Digital Mars 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
version(StdDdoc)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 $(D 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 $(D GetLastError)) occur.
41 */
42 class WindowsException : Exception
43 {
44 private alias DWORD = int;
45 final @property DWORD code(); /// $(D GetLastError)'s return value.
46 this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted;
47 }
48
49 /++
50 If $(D !!value) is true, $(D value) is returned. Otherwise,
51 $(D new WindowsException(GetLastError(), msg)) is thrown.
52 $(D WindowsException) assumes that the last operation set
53 $(D 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.windows;
69 import std.array : appender;
70 import std.conv : to;
71 import std.format : formattedWrite;
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 if (!putSysError(errCode, buf, MAKELANGID(langId, subLangId)))
83 {
84 throw new Exception(
85 "failed getting error string for WinAPI error code: " ~
86 sysErrorString(GetLastError()));
87 }
88
89 return buf.data;
90 }
91
putSysError(Writer)92 bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0)
93 {
94 wchar *lpMsgBuf = null;
95 auto res = FormatMessageW(
96 FORMAT_MESSAGE_ALLOCATE_BUFFER |
97 FORMAT_MESSAGE_FROM_SYSTEM |
98 FORMAT_MESSAGE_IGNORE_INSERTS,
99 null,
100 code,
101 langId,
102 cast(LPWSTR)&lpMsgBuf,
103 0,
104 null);
105 scope(exit) if (lpMsgBuf) LocalFree(lpMsgBuf);
106
107 if (lpMsgBuf)
108 {
109 import std.string : strip;
110 w.put(lpMsgBuf[0 .. res].strip());
111 return true;
112 }
113 else
114 return false;
115 }
116
117
118 class WindowsException : Exception
119 {
120 import core.sys.windows.windows : DWORD;
121
code()122 final @property DWORD code() { return _code; } /// $(D GetLastError)'s return value.
123 private DWORD _code;
124
125 this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted
126 {
127 _code = code;
128
129 auto buf = appender!string();
130
131 if (str != null)
132 {
133 buf.put(str);
134 if (code)
135 buf.put(": ");
136 }
137
138 if (code)
139 {
140 auto success = putSysError(code, buf);
141 formattedWrite(buf, success ? " (error %d)" : "Error %d", code);
142 }
143
144 super(buf.data, file, line);
145 }
146 }
147
148
149 T wenforce(T, S)(T value, lazy S msg = null,
150 string file = __FILE__, size_t line = __LINE__)
151 if (isSomeString!S)
152 {
153 if (!value)
154 throw new WindowsException(GetLastError(), to!string(msg), file, line);
155 return value;
156 }
157
wenforce(T)158 T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file = __FILE__, size_t line = __LINE__)
159 {
160 if (condition)
161 return condition;
162 string names;
163 if (!name)
164 {
165 static string trustedToString(const(wchar)* stringz) @trusted
166 {
167 import core.stdc.wchar_ : wcslen;
168 import std.conv : to;
169 auto len = wcslen(stringz);
170 return to!string(stringz[0 .. len]);
171 }
172
173 names = trustedToString(namez);
174 }
175 else
176 names = to!string(name);
177 throw new WindowsException(GetLastError(), names, file, line);
178 }
179
version(Windows)180 version (Windows)
181 @system unittest
182 {
183 import std.algorithm.searching : startsWith, endsWith;
184 import std.exception;
185 import std.string;
186
187 auto e = collectException!WindowsException(
188 DeleteFileA("unexisting.txt").wenforce("DeleteFile")
189 );
190 assert(e.code == ERROR_FILE_NOT_FOUND);
191 assert(e.msg.startsWith("DeleteFile: "));
192 // can't test the entire message, as it depends on Windows locale
193 assert(e.msg.endsWith(" (error 2)"));
194
195 // Test code zero
196 e = new WindowsException(0);
197 assert(e.msg == "");
198
199 e = new WindowsException(0, "Test");
200 assert(e.msg == "Test");
201 }
202