Skip to content

Instantly share code, notes, and snippets.

@thedeemon
Last active June 13, 2016 16:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thedeemon/3c2989b76004fafe9aa0 to your computer and use it in GitHub Desktop.
Save thedeemon/3c2989b76004fafe9aa0 to your computer and use it in GitHub Desktop.
module comptr;
import std.stdio, core.sys.windows.com, core.sys.windows.windows, std.exception, std.meta, std.traits;
//version = comptrtalk;
GUID Guid(string str)() {
static assert(str.length==36, "Guid string must be 36 chars long");
enum GUIDstring = "GUID(0x" ~ str[0..8] ~ ", 0x" ~ str[9..13] ~ ", 0x" ~ str[14..18] ~
", [0x" ~ str[19..21] ~ ", 0x" ~ str[21..23] ~ ", 0x" ~ str[24..26] ~ ", 0x" ~ str[26..28]
~ ", 0x" ~ str[28..30] ~ ", 0x" ~ str[30..32] ~ ", 0x" ~ str[32..34] ~ ", 0x" ~ str[34..36] ~ "])";
return mixin(GUIDstring);
}
struct ComPtr(Interface) //taken from VisualD, then modified
{
protected Interface ptr;
protected string name;
this(Interface i, bool doref = true) {
version(comptrtalk) writeln("ComPtr this (Interface)");
ptr = i;
if(ptr && doref)
ptr.AddRef();
}
static if (!is(Interface==IUnknown)) {
this(IUnknown i) {
version(comptrtalk) writeln("ComPtr this(IUnknown), doing qi_cast");
ptr = qi_cast!(Interface)(i);
}
this(T)(ComPtr!T p) {
version(comptrtalk) writeln("ComPtr this(IUnknown), doing qi_cast");
ptr = qi_cast!(Interface)(p.raw);
}
this(ref CLSID clsid, string name_ = "") {
name = name_;
version(comptrtalk) writeln("ComPtr this (CLSID) ", name);
auto hr = CoCreateInstance(&clsid, null, CLSCTX_ALL, &Interface.iid, cast(void**)&ptr);
version(comptrtalk) writefln("ComPtr create instance: %X", hr);
}
}
this(this) {
version(comptrtalk) writeln("this(this) ", name);
if (ptr) ptr.AddRef();
}
~this() {
version(comptrtalk) writeln("ComPtr ~this ", name);
if(ptr)
ptr.Release();
}
Interface detach() {
Interface p = ptr;
ptr = null;
return p;
}
void opAssign(Interface i) {
version(comptrtalk) writeln("ComPtr opAssign = Interface");
if(ptr)
ptr.Release();
ptr = i;
if(ptr)
ptr.AddRef();
}
void opAssign(IUnknown i) {
version(comptrtalk) writeln("ComPtr opAssign = IUnknown");
if(ptr)
ptr.Release();
ptr = qi_cast!(Interface)(i);
}
Interface opCast(T:Interface)() { return ptr; }
Interface raw() { return ptr; }
bool opCast(T:bool)() { return ptr !is null; }
auto opDispatch(string fn, Ts...)(Ts vs) {
import std.format;
enum vss = unwrapPtrs!Ts;
alias RetType = ReturnType!(mixin("ptr."~fn));
static if (is(RetType == HRESULT)) {
HRESULT hr = mixin("ptr." ~ fn ~ "(" ~ vss ~ ")");
if (hr < 0) {
string msg = format("%s%s::%s returned %X",
name.length > 0 ? name ~ " calling " : "",
Interface.stringof, fn, hr);
throw new COMException(hr, msg);
}
return hr;
} else
return mixin("ptr." ~ fn ~ "(" ~ vss ~ ")");
}
}
enum isComPtr(T) = is(T == ComPtr!A, A);
string unwrapPtrs(Ts...)() {
import std.string : format;
import std.array : join;
string[] res;
res.length = Ts.length;
foreach(n, T; Ts) {
static if (isComPtr!T) res[n] = format("vs[%d].raw", n);
else res[n] = format("vs[%d]", n);
}
return res.join(", ");
}
I qi_cast(I)(IUnknown obj)
{
I iobj;
if (obj is null) return null;
static if (!is(I == IUnknown)) {
if(obj.QueryInterface(&I.iid, cast(void**)&iobj) == S_OK)
return iobj;
} else {
obj.AddRef();
return obj;
}
return null;
}
class COMException : Exception {
HRESULT hr;
this(int hrcode, string msg="hr is no good") { super(msg); hr = hrcode; }
this(string msg, string file, size_t line) { super(msg, file, line); hr = E_POINTER; }
}
HRESULT checkHR(HRESULT hr, string msg = "hr is no good") {
if (hr < 0) throw new COMException(hr, msg);
return hr;
}
auto doing(lazy HRESULT smth, string desc) {
try {
return smth;
} catch(COMException ex) {
throw new COMException(ex.hr, desc ~ ": " ~ ex.msg);
}
}
auto require(T)(ComPtr!T x) {
enforce!COMException(x);
return x;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment