mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 05:30:33 +03:00
257 lines
7.1 KiB
D
257 lines
7.1 KiB
D
// Written in the D programming language.
|
|
|
|
/**
|
|
* Convert Win32 error code to string.
|
|
*
|
|
* Copyright: Copyright The D Language Foundation" 2006 - 2013.
|
|
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
* Authors: $(HTTP digitalmars.com, Walter Bright)
|
|
* Credits: Based on code written by Regan Heath
|
|
*/
|
|
/* Copyright The D Language Foundation" 2006 - 2013.
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
module std.windows.syserror;
|
|
import std.traits : isSomeString;
|
|
|
|
version (StdDdoc)
|
|
{
|
|
private
|
|
{
|
|
alias DWORD = uint;
|
|
enum LANG_NEUTRAL = 0, SUBLANG_DEFAULT = 1;
|
|
}
|
|
|
|
/** Query the text for a Windows error code, as returned by
|
|
$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx,
|
|
`GetLastError`), as a D string.
|
|
*/
|
|
string sysErrorString(
|
|
DWORD errCode,
|
|
// MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language
|
|
int langId = LANG_NEUTRAL,
|
|
int subLangId = SUBLANG_DEFAULT) @trusted;
|
|
|
|
/*********************
|
|
Thrown if errors that set
|
|
$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx,
|
|
`GetLastError`) occur.
|
|
*/
|
|
class WindowsException : Exception
|
|
{
|
|
private alias DWORD = int;
|
|
final @property DWORD code(); /// `GetLastError`'s return value.
|
|
this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted;
|
|
}
|
|
|
|
/++
|
|
If `!!value` is true, `value` is returned. Otherwise,
|
|
$(D new WindowsException(GetLastError(), msg)) is thrown.
|
|
`WindowsException` assumes that the last operation set
|
|
`GetLastError()` appropriately.
|
|
|
|
Example:
|
|
--------------------
|
|
wenforce(DeleteFileA("junk.tmp"), "DeleteFile failed");
|
|
--------------------
|
|
+/
|
|
T wenforce(T, S)(T value, lazy S msg = null,
|
|
string file = __FILE__, size_t line = __LINE__) @safe
|
|
if (isSomeString!S);
|
|
}
|
|
else:
|
|
|
|
version (Windows):
|
|
|
|
import core.sys.windows.winbase, core.sys.windows.winnt;
|
|
import std.array : appender, Appender;
|
|
import std.conv : to, toTextRange, text;
|
|
import std.exception;
|
|
|
|
string sysErrorString(
|
|
DWORD errCode,
|
|
// MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language
|
|
int langId = LANG_NEUTRAL,
|
|
int subLangId = SUBLANG_DEFAULT) @trusted
|
|
{
|
|
auto buf = appender!string();
|
|
|
|
wenforce(
|
|
// Ignore unlikely UTF decoding errors, always report the actual error (`errCode`)
|
|
putSysError(errCode, buf, MAKELANGID(langId, subLangId)).ifThrown(false),
|
|
text("Could not fetch error string for WinAPI code ", errCode)
|
|
);
|
|
|
|
return buf.data;
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
import std.algorithm.searching;
|
|
|
|
assert(sysErrorString(ERROR_PATH_NOT_FOUND) !is null);
|
|
|
|
const msg = collectExceptionMsg!WindowsException(sysErrorString(DWORD.max));
|
|
assert(msg.startsWith(`Could not fetch error string for WinAPI code 4294967295: `));
|
|
}
|
|
|
|
bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0)
|
|
{
|
|
wchar *lpMsgBuf = null;
|
|
auto res = FormatMessageW(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
null,
|
|
code,
|
|
langId,
|
|
cast(LPWSTR)&lpMsgBuf,
|
|
0,
|
|
null);
|
|
scope(exit) if (lpMsgBuf) LocalFree(lpMsgBuf);
|
|
|
|
if (lpMsgBuf)
|
|
{
|
|
import std.string : strip;
|
|
w.put(lpMsgBuf[0 .. res].strip());
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
class WindowsException : Exception
|
|
{
|
|
import core.sys.windows.windef : DWORD;
|
|
|
|
final @property DWORD code() { return _code; } /// `GetLastError`'s return value.
|
|
private DWORD _code;
|
|
|
|
this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted
|
|
{
|
|
_code = code;
|
|
|
|
auto buf = appender!(char[]);
|
|
|
|
if (str != null)
|
|
{
|
|
buf.put(str);
|
|
if (code)
|
|
buf.put(": ");
|
|
}
|
|
|
|
if (code && writeErrorMessage(code, buf))
|
|
{
|
|
buf.put(" (error ");
|
|
toTextRange(code, buf);
|
|
buf.put(')');
|
|
}
|
|
|
|
super(cast(immutable) buf.data, file, line);
|
|
}
|
|
}
|
|
|
|
/// Writes the error string associated to `code` into `buf`.
|
|
/// Writes `Error <code>` when the error message lookup fails
|
|
private bool writeErrorMessage(DWORD code, ref Appender!(char[]) buf) nothrow
|
|
{
|
|
bool success;
|
|
try
|
|
{
|
|
// Reset the buffer to undo partial changes
|
|
const len = buf[].length;
|
|
scope (failure) buf.shrinkTo(len);
|
|
|
|
success = putSysError(code, buf);
|
|
}
|
|
catch (Exception) {}
|
|
|
|
// Write the error code instead if we couldn't find the string
|
|
if (!success)
|
|
{
|
|
buf.put("Error ");
|
|
toTextRange(code, buf);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
T wenforce(T, S = string)(T value, lazy S msg = null,
|
|
string file = __FILE__, size_t line = __LINE__)
|
|
if (isSomeString!S)
|
|
{
|
|
if (!value)
|
|
throw new WindowsException(GetLastError(), to!string(msg), file, line);
|
|
return value;
|
|
}
|
|
|
|
T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file = __FILE__, size_t line = __LINE__)
|
|
{
|
|
if (condition)
|
|
return condition;
|
|
string names;
|
|
if (!name)
|
|
{
|
|
static string trustedToString(const(wchar)* stringz) @trusted
|
|
{
|
|
import core.stdc.wchar_ : wcslen;
|
|
import std.conv : to;
|
|
auto len = wcslen(stringz);
|
|
return to!string(stringz[0 .. len]);
|
|
}
|
|
|
|
names = trustedToString(namez);
|
|
}
|
|
else
|
|
names = to!string(name);
|
|
throw new WindowsException(GetLastError(), names, file, line);
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
import std.algorithm.searching : startsWith, endsWith;
|
|
import std.string;
|
|
|
|
auto e = collectException!WindowsException(
|
|
DeleteFileA("unexisting.txt").wenforce("DeleteFile")
|
|
);
|
|
assert(e.code == ERROR_FILE_NOT_FOUND);
|
|
assert(e.msg.startsWith("DeleteFile: "));
|
|
// can't test the entire message, as it depends on Windows locale
|
|
assert(e.msg.endsWith(" (error 2)"));
|
|
|
|
// Test code zero
|
|
e = new WindowsException(0);
|
|
assert(e.msg == "");
|
|
|
|
e = new WindowsException(0, "Test");
|
|
assert(e.msg == "Test");
|
|
}
|
|
|
|
@safe nothrow unittest
|
|
{
|
|
import std.algorithm.searching : endsWith;
|
|
|
|
auto e = new WindowsException(ERROR_FILE_NOT_FOUND);
|
|
assert(e.msg.endsWith("(error 2)"));
|
|
|
|
e = new WindowsException(DWORD.max);
|
|
assert(e.msg == "Error 4294967295");
|
|
}
|
|
|
|
/// Tries to translate an error code from the Windows API to the corresponding
|
|
/// error message. Returns `Error <code>` on failure
|
|
package (std) string generateSysErrorMsg(DWORD errCode = GetLastError()) nothrow @trusted
|
|
{
|
|
auto buf = appender!(char[]);
|
|
cast(void) writeErrorMessage(errCode, buf);
|
|
return cast(immutable) buf[];
|
|
}
|
|
|
|
nothrow @safe unittest
|
|
{
|
|
assert(generateSysErrorMsg(ERROR_PATH_NOT_FOUND) !is null);
|
|
assert(generateSysErrorMsg(DWORD.max) == "Error 4294967295");
|
|
}
|