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) @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;
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 
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.windef : DWORD;
121 
122     final @property DWORD code() { return _code; } /// `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 
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 
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 }
Suggestion Box / Bug Report