blob: 73863607dd10d89761cc897a71d544bee5cc0f72 [file] [log] [blame]
// Written in the D programming language.
/**
* Convert Win32 error code to string.
*
* Copyright: Copyright Digital Mars 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 Digital Mars 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,
$(D 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,
$(D GetLastError)) occur.
*/
class WindowsException : Exception
{
private alias DWORD = int;
final @property DWORD code(); /// $(D GetLastError)'s return value.
this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted;
}
/++
If $(D !!value) is true, $(D value) is returned. Otherwise,
$(D new WindowsException(GetLastError(), msg)) is thrown.
$(D WindowsException) assumes that the last operation set
$(D 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.windows;
import std.array : appender;
import std.conv : to;
import std.format : formattedWrite;
import std.windows.charset;
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();
if (!putSysError(errCode, buf, MAKELANGID(langId, subLangId)))
{
throw new Exception(
"failed getting error string for WinAPI error code: " ~
sysErrorString(GetLastError()));
}
return buf.data;
}
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.windows : DWORD;
final @property DWORD code() { return _code; } /// $(D GetLastError)'s return value.
private DWORD _code;
this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted
{
_code = code;
auto buf = appender!string();
if (str != null)
{
buf.put(str);
if (code)
buf.put(": ");
}
if (code)
{
auto success = putSysError(code, buf);
formattedWrite(buf, success ? " (error %d)" : "Error %d", code);
}
super(buf.data, file, line);
}
}
T wenforce(T, S)(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);
}
version (Windows)
@system unittest
{
import std.algorithm.searching : startsWith, endsWith;
import std.exception;
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");
}