1 /++
2 	My old com helper code. I haven't used it for years.
3 +/
4 module arsd.comhelpers;
5 
6 /+
7 	see: program\comtest.d on the laptop.
8 
9 	as administrator: from program\cs
10 	c:\Windows\Microsoft.NEt\Framework64\v4.0.30319\regasm.exe /regfile /codebase test.dll
11 
12 	sn -k key.snk
13 	program\cs\makefile
14 
15 	test.js in there shows it form wsh too
16 
17 	i can make it work through IDispatch easily enough, though
18 	ideally you'd have a real interface, that requires cooperation
19 	that the idispatch doesn't thanks to .net doing it for us.
20 
21 	passing other objects should work too btw thanks to idispatch
22 	in the variants... not sure about arrays tho
23 
24 	and then fully dynamic can be done with opDispatch for teh lulz.
25 +/
26 
27 version(Windows):
28 
29 import core.sys.windows.windows;
30 import core.sys.windows.com;
31 import core.sys.windows.oaidl;
32 
33 public import core.stdc.string;
34 import core.atomic;
35 
36 pragma(lib, "advapi32");
37 pragma(lib, "uuid");
38 pragma(lib, "ole32");
39 pragma(lib, "oleaut32");
40 
41 
42 /* Attributes that help with automation */
43 
44 static immutable struct ComGuid {
45 	GUID guid;
46 }
47 
48 bool hasGuidAttribute(T)() {
49 	foreach(attr; __traits(getAttributes, T))
50 		static if(is(typeof(attr) == ComGuid))
51 			return true;
52 	return false;
53 }
54 
55 template getGuidAttribute(T) {
56 	static ComGuid helper() {
57 		foreach(attr; __traits(getAttributes, T))
58 			static if(is(typeof(attr) == ComGuid))
59 				return attr;
60 		assert(0);
61 	}
62 	__gshared static immutable getGuidAttribute = helper();
63 }
64 
65 
66 /* COM CLIENT CODE */
67 
68 __gshared int coInitializeCalled;
69 static ~this() {
70 	CoFreeUnusedLibraries();
71 	if(coInitializeCalled) {
72 		CoUninitialize();
73 		coInitializeCalled--;
74 	}
75 }
76 void initializeCom() {
77 	if(coInitializeCalled)
78 		return;
79 
80 	/*
81 	// Make sure COM is the right version
82 	auto dwVer = CoBuildVersion();
83 
84 	if (rmm != HIWORD(dwVer))
85 		throw new Exception("Incorrect OLE 2 version number\n");
86 	*/
87 
88 	auto hr = CoInitialize(null);
89 
90 	if (FAILED(hr))
91 		throw new Exception("OLE 2 failed to initialize\n");
92 
93 	coInitializeCalled++;
94 }
95 
96 struct AutoComPtr(T) {
97 	T t;
98 	this(T t) {
99 		this.t = t;
100 	}
101 	this(this) {
102 		t.AddRef();
103 	}
104 	~this() {
105 		t.Release();
106 	}
107 	alias t this;
108 }
109 
110 /*
111 	If you want to do self-registration:
112 
113 	if(dll_regserver("filename.dll", 1) == 0) {
114 		scope(exit)
115 			dll_regserver("filename.dll", 0);
116 		// use it
117 	}
118 */
119 
120 // note that HKEY_CLASSES_ROOT\pretty name\CLSID has the guid
121 
122 /// Create a COM object. the string params are GUID literals that i mixin (this sux i know)
123 /// or if the interface has no IID it will try to IDispatch it
124 /// or you can request a fully dynamic version via opDispatch.
125 /// note i can try `import core.sys.windows.uuid; IID_IDispatch` for example to generically look up ones from the system if they are not attached and come from the windows namespace
126 AutoComPtr!T createObject(T, string iidStr = null)(GUID classId) {
127 	initializeCom();
128 
129 	static if(iidStr == null) {
130 		auto iid = getGuidAttribute!(T).guid;
131 	} else
132 		auto iid = mixin(iidStr);
133 
134 	T obj;
135 
136 	auto hr = CoCreateInstance(&classId, null, CLSCTX_INPROC_SERVER, &iid, cast(void**) &obj);
137 	import std.format;
138 	if(FAILED(hr))
139 		throw new Exception("Failed to create object " ~ format("%08x", hr));
140 
141 	return AutoComPtr!T(obj);
142 }
143 
144 
145 // FIXME: add one to get by ProgID rather than always guid
146 // FIXME: add a dynamic com object that uses IDispatch
147 
148 
149 /* COM SERVER CODE */
150 
151 T getFromVariant(T)(VARIANT arg) {
152 	import std.traits;
153 	import std.conv;
154 	static if(is(T == int)) {
155 		if(arg.vt == 3)
156 			return arg.intVal;
157 	} else static if(is(T == string)) {
158 		if(arg.vt == 8) {
159 			auto str = arg.bstrVal;
160 			return to!string(str[0 .. SysStringLen(str)]);
161 		}
162 	} else static if(is(T == IDispatch)) {
163 		if(arg.vt == 9)
164 			return arg.pdispVal;
165 	}
166 	throw new Exception("Type mismatch, needed "~ T.stringof ~"got " ~ to!string(arg.vt));
167 	assert(0);
168 }
169 
170 mixin template IDispatchImpl() {
171 	override HRESULT GetIDsOfNames( REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) {
172 		if(cNames == 0)
173 			return DISP_E_UNKNOWNNAME;
174 
175 		char[256] buffer;
176 		auto want = oleCharsToString(buffer, rgszNames[0]);
177 		foreach(idx, member; __traits(allMembers, typeof(this))) {
178 			if(member == want) {
179 				rgDispId[0] = idx + 1;
180 				return S_OK;
181 			}
182 		}
183 		return DISP_E_UNKNOWNNAME;
184 	}
185 
186 	override HRESULT GetTypeInfoCount(UINT* i) { *i = 0; return S_OK; }
187 	override HRESULT GetTypeInfo(UINT i, LCID l, LPTYPEINFO* p) { *p = null; return S_OK; }
188 	override HRESULT Invoke(DISPID dispIdMember, REFIID reserved, LCID locale, WORD wFlags, DISPPARAMS* params, VARIANT* result, EXCEPINFO* except, UINT* argErr) {
189 	// wFlags == 1 function call
190 	// wFlags == 2 property getter
191 	// wFlags == 4 property setter
192 		foreach(idx, member; __traits(allMembers, typeof(this))) {
193 			if(idx + 1 == dispIdMember) {
194 			static if(is(typeof(__traits(getMember, this, member)) == function))
195 				try {
196 					import std.traits;
197 					ParameterTypeTuple!(__traits(getMember, this, member)) args;
198 					alias argsStc = ParameterStorageClassTuple!(__traits(getMember, this, member));
199 
200 					static if(argsStc.length >= 1 && argsStc[0] == ParameterStorageClass.out_) {
201 						// the return value is often the first out param
202 						typeof(args[0]) returnedValue;
203 
204 						if(params !is null) {
205 							assert(params.cNamedArgs == 0); // FIXME
206 
207 							if(params.cArgs < args.length - 1)
208 								return DISP_E_BADPARAMCOUNT;
209 
210 							foreach(aidx, arg; args[1 .. $])
211 								args[1 + aidx] = getFromVariant!(typeof(arg))(params.rgvarg[aidx]);
212 						}
213 
214 						static if(is(ReturnType!(__traits(getMember, this, member)) == void)) {
215 							__traits(getMember, this, member)(returnedValue, args[1 .. $]);
216 						} else {
217 							auto returned = __traits(getMember, this, member)(returnedValue, args[1 .. $]);
218 							// FIXME: it probably returns HRESULT so we should forward that or something.
219 						}
220 
221 						if(result !is null) {
222 							static if(argsStc.length >= 1 && argsStc[0] == ParameterStorageClass.out_) {
223 								result.vt = 3; // int
224 								result.intVal = returnedValue;
225 							}
226 						}
227 					} else {
228 
229 						if(params !is null) {
230 							assert(params.cNamedArgs == 0); // FIXME
231 							if(params.cArgs < args.length)
232 								return DISP_E_BADPARAMCOUNT;
233 							foreach(aidx, arg; args)
234 								args[aidx] = getFromVariant!(typeof(arg))(params.rgvarg[aidx]);
235 						}
236 
237 						// no return value of note (just HRESULT at most)
238 						static if(is(ReturnType!(__traits(getMember, this, member)) == void)) {
239 							__traits(getMember, this, member)(args);
240 						} else {
241 							auto returned = __traits(getMember, this, member)(args);
242 							// FIXME: it probably returns HRESULT so we should forward that or something.
243 						}
244 					}
245 
246 					return S_OK;
247 				} catch(Throwable e) {
248 					// FIXME: fill in the exception info
249 					if(except !is null) {
250 						except.wCode = 1;
251 						import std.utf;
252 						except.bstrDescription = SysAllocString(toUTFz!(wchar*)(e.toString()));
253 						except.bstrSource = SysAllocString("amazing"w.ptr);
254 					}
255 					return DISP_E_EXCEPTION;
256 				}
257 			}
258 		}
259 
260 		return DISP_E_MEMBERNOTFOUND;
261 	}
262 }
263 
264 mixin template ComObjectImpl() {
265 protected:
266 	IUnknown m_pUnkOuter;       // Controlling unknown
267 	PFNDESTROYED m_pfnDestroy;          // To call on closure
268 
269     /*
270      *  pUnkOuter       LPUNKNOWN of a controlling unknown.
271      *  pfnDestroy      PFNDESTROYED to call when an object
272      *                  is destroyed.
273      */
274 	public this(IUnknown pUnkOuter, PFNDESTROYED pfnDestroy) {
275 		m_pUnkOuter  = pUnkOuter;
276 		m_pfnDestroy = pfnDestroy;
277 	}
278 
279 	~this() {
280 		//MessageBoxA(null, "CHello.~this()", null, MB_OK);
281 	}
282 
283 	// Note: you can implement your own Init along with this mixin template and your function will automatically override this one
284     /*
285      *  Performs any intialization of a CHello that's prone to failure
286      *  that we also use internally before exposing the object outside.
287      * Return Value:
288      *  BOOL            true if the function is successful,
289      *                  false otherwise.
290      */
291 	public BOOL Init() {
292 		//MessageBoxA(null, "CHello.Init()", null, MB_OK);
293 		return true;
294 	}
295 
296 
297 	public
298 	override HRESULT QueryInterface(const (IID)*riid, LPVOID *ppv) {
299 		// wchar[200] lol; auto got = StringFromGUID2(riid, lol.ptr, lol.length); import std.conv;
300 		//MessageBoxA(null, toStringz("CHello.QueryInterface(g: "~to!string(lol[0 .. got])~")"), null, MB_OK);
301 
302 		assert(ppv !is null);
303 		*ppv = null;
304 
305 		import std.traits;
306 		foreach(iface; InterfacesTuple!(typeof(this))) {
307 			static if(hasGuidAttribute!iface()) {
308 				auto guid = getGuidAttribute!iface;
309 				if(*riid == guid.guid) {
310 					*ppv = cast(void*) cast(iface) this;
311 					break;
312 				}
313 			} else static if(is(iface == IUnknown)) {
314 				if (IID_IUnknown == *riid) {
315 					*ppv = cast(void*) cast(IUnknown) this;
316 					break;
317 				}
318 			} else static if(is(iface == IDispatch)) {
319 				if (IID_IDispatch == *riid) {
320 					*ppv = cast(void*) cast(IDispatch) this;
321 					break;
322 				}
323 			}
324 		}
325 
326 		if(*ppv !is null) {
327 			AddRef();
328 			return NOERROR;
329 		} else {
330 			return E_NOINTERFACE;
331 		}
332 	}
333 
334 	public
335 	extern(Windows) ULONG AddRef() {
336 		import core.atomic;
337 		return atomicOp!"+="(*cast(shared)&count, 1);
338 	}
339 
340 	public
341 	extern(Windows) ULONG Release() {
342 		import core.atomic;
343 		LONG lRef = atomicOp!"-="(*cast(shared)&count, 1);
344 		if (lRef == 0) {
345 			// free object
346 
347 			/*
348 			* Tell the housing that an object is going away so it can
349 			* shut down if appropriate.
350 			*/
351 			//MessageBoxA(null, "CHello Destroy()", null, MB_OK);
352 
353 			if (m_pfnDestroy)
354 				(*m_pfnDestroy)();
355 
356 			// delete this;
357 			return 0;
358 
359 
360 			// If we delete this object, then the postinvariant called upon
361 			// return from Release() will fail.
362 			// Just let the GC reap it.
363 			//delete this;
364 
365 			return 0;
366 		}
367 
368 		return cast(ULONG)lRef;
369 	}
370 
371 	LONG count = 0;             // object reference count
372 
373 }
374 
375 
376 
377 
378 // Type for an object-destroyed callback
379 alias void function() PFNDESTROYED;
380 
381 extern (C)
382 {
383 	void rt_init();
384 	void rt_term();
385 	void gc_init();
386 	void gc_term();
387 }
388 
389 
390 // This class factory object creates Hello objects.
391 class ClassFactory(Class) : IClassFactory {
392 	extern (Windows) :
393 
394 	// IUnknown members
395 	override HRESULT QueryInterface(const (IID)*riid, LPVOID *ppv) {
396 		if (IID_IUnknown == *riid) {
397 			*ppv = cast(void*) cast(IUnknown) this;
398 		}
399 		else if (IID_IClassFactory == *riid) {
400 			*ppv = cast(void*) cast(IClassFactory) this;
401 		}
402 		else {
403 			*ppv = null;
404 			return E_NOINTERFACE;
405 		}
406 
407 		AddRef();
408 		return NOERROR;
409 	}
410 
411 	LONG count = 0;             // object reference count
412 	ULONG AddRef() {
413 		return atomicOp!"+="(*cast(shared)&count, 1);
414 	}
415 
416 	ULONG Release() {
417 		return atomicOp!"-="(*cast(shared)&count, 1);
418 	}
419 
420 	// IClassFactory members
421 	override HRESULT CreateInstance(IUnknown pUnkOuter, IID*riid, LPVOID *ppvObj) {
422 		HRESULT hr;
423 
424 		*ppvObj = null;
425 		hr      = E_OUTOFMEMORY;
426 
427 		// Verify that a controlling unknown asks for IUnknown
428 		if (null !is pUnkOuter && IID_IUnknown == *riid)
429 			return CLASS_E_NOAGGREGATION;
430 
431 		// Create the object passing function to notify on destruction.
432 		auto pObj = new Class(pUnkOuter, &ObjectDestroyed);
433 
434 		if (!pObj) {
435 			MessageBoxA(null, "null", null, 0);
436 			return hr;
437 		}
438 
439 		if (pObj.Init()) {
440 			hr = pObj.QueryInterface(riid, ppvObj);
441 		}
442 
443 		// Kill the object if initial creation or Init failed.
444 		if (FAILED(hr))
445 			delete pObj;
446 		else
447 			g_cObj++;
448 
449 		return hr;
450 	}
451 
452 	HRESULT LockServer(BOOL fLock) {
453 		//MessageBoxA(null, "CHelloClassFactory.LockServer()", null, MB_OK);
454 
455 		if (fLock)
456 			g_cLock++;
457 		else
458 			g_cLock--;
459 
460 		return NOERROR;
461 	}
462 }
463 __gshared ULONG g_cLock=0;
464 __gshared ULONG g_cObj =0;
465 
466 /*
467  * ObjectDestroyed
468  *
469  * Purpose:
470  *  Function for the Hello object to call when it gets destroyed.
471  *  Since we're in a DLL we only track the number of objects here,
472  *  letting DllCanUnloadNow take care of the rest.
473  */
474 
475 extern (D) void ObjectDestroyed()
476 {
477     //MessageBoxA(null, "ObjectDestroyed()", null, MB_OK);
478     g_cObj--;
479 }
480 
481 
482 char[] oleCharsToString(char[] buffer, OLECHAR* chars) {
483 	auto c = cast(wchar*) chars;
484 	auto orig = c;
485 
486 	size_t len = 0;
487 	while(*c) {
488 		len++;
489 		c++;
490 	}
491 
492 	auto c2 = orig[0 .. len];
493 	int blen;
494 	foreach(ch; c2) {
495 		// FIXME breaks for non-ascii
496 		assert(ch < 127);
497 		buffer[blen] = cast(char) ch;
498 		blen++;
499 	}
500 
501 	return buffer[0 .. blen];
502 }
503 
504 
505 // usage: mixin ComServerMain!(CHello, CLSID_Hello, "Hello", "1.0");
506 mixin template ComServerMain(Class, string progId, string ver) {
507 	static assert(hasGuidAttribute!Class, "Add a @ComGuid(GUID()) to your class");
508 
509 	__gshared HINSTANCE g_hInst;
510 
511 	// initializing the runtime can fail on Windows XP when called via regsvr32...
512 
513 	extern (Windows)
514 	BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) {
515 		import core.sys.windows.dll;
516 		g_hInst = hInstance;
517 
518 		switch (ulReason) {
519 			case DLL_PROCESS_ATTACH:
520 				return dll_process_attach(hInstance, true);
521 			break;
522 			case DLL_THREAD_ATTACH:
523 				dll_thread_attach(true, true);
524 			break;
525 			case DLL_PROCESS_DETACH:
526 				dll_process_detach(hInstance, true);
527 			break;
528 
529 			case DLL_THREAD_DETACH:
530 				return dll_thread_detach(true, true);
531 			break;
532 
533 			default:
534 				assert(0);
535 		}
536 
537 		return true;
538 	}
539 
540 	/*
541 	 * DllGetClassObject
542 	 *
543 	 * Purpose:
544 	 *  Provides an IClassFactory for a given CLSID that this DLL is
545 	 *  registered to support.  This DLL is placed under the CLSID
546 	 *  in the registration database as the InProcServer.
547 	 *
548 	 * Parameters:
549 	 *  clsID           REFCLSID that identifies the class factory
550 	 *                  desired.  Since this parameter is passed this
551 	 *                  DLL can handle any number of objects simply
552 	 *                  by returning different class factories here
553 	 *                  for different CLSIDs.
554 	 *
555 	 *  riid            REFIID specifying the interface the caller wants
556 	 *                  on the class object, usually IID_ClassFactory.
557 	 *
558 	 *  ppv             LPVOID * in which to return the interface
559 	 *                  pointer.
560 	 *
561 	 * Return Value:
562 	 *  HRESULT         NOERROR on success, otherwise an error code.
563 	 */
564 	pragma(mangle, "DllGetClassObject")
565 	extern(Windows)
566 	HRESULT DllGetClassObject(CLSID* rclsid, IID* riid, LPVOID* ppv) {
567 		HRESULT hr;
568 		ClassFactory!Class pObj;
569 
570 		//MessageBoxA(null, "DllGetClassObject()", null, MB_OK);
571 
572 		// printf("DllGetClassObject()\n");
573 
574 		if (clsid != *rclsid)
575 			return E_FAIL;
576 
577 		pObj = new ClassFactory!Class();
578 
579 		if (!pObj)
580 			return E_OUTOFMEMORY;
581 
582 		hr = pObj.QueryInterface(riid, ppv);
583 
584 		if (FAILED(hr))
585 			delete pObj;
586 
587 		return hr;
588 	}
589 
590 	/*
591 	 *  Answers if the DLL can be freed, that is, if there are no
592 	 *  references to anything this DLL provides.
593 	 *
594 	 * Return Value:
595 	 *  BOOL            true if nothing is using us, false otherwise.
596 	 */
597 	pragma(mangle, "DllCanUnloadNow")
598 	extern(Windows)
599 	HRESULT DllCanUnloadNow() {
600 		SCODE sc;
601 
602 		//MessageBoxA(null, "DllCanUnloadNow()", null, MB_OK);
603 
604 		// Any locks or objects?
605 		sc = (0 == g_cObj && 0 == g_cLock) ? S_OK : S_FALSE;
606 		return sc;
607 	}
608 
609 	static immutable clsid = getGuidAttribute!Class.guid;
610 
611 	/*
612 	 *  Instructs the server to create its own registry entries
613 	 *
614 	 * Return Value:
615 	 *  HRESULT         NOERROR if registration successful, error
616 	 *                  otherwise.
617 	 */
618 	pragma(mangle, "DllRegisterServer")
619 	extern(Windows)
620 	HRESULT DllRegisterServer() {
621 		char[128] szID;
622 		char[128] szCLSID;
623 		char[512] szModule;
624 
625 		// Create some base key strings.
626 		MessageBoxA(null, "DllRegisterServer", null, MB_OK);
627 		auto len = StringFromGUID2(&clsid, cast(LPOLESTR) szID, 128);
628 		unicode2ansi(szID.ptr);
629 		szID[len] = 0;
630 
631 		//MessageBoxA(null, toStringz("DllRegisterServer("~szID[0 .. len] ~")"), null, MB_OK);
632 
633 		strcpy(szCLSID.ptr, "CLSID\\");
634 		strcat(szCLSID.ptr, szID.ptr);
635 
636 		char[200] partialBuffer;
637 		partialBuffer[0 .. progId.length] = progId[];
638 		partialBuffer[progId.length] = 0;
639 		auto partial = partialBuffer.ptr;
640 
641 		char[200] fullBuffer;
642 		fullBuffer[0 .. progId.length] = progId[];
643 		fullBuffer[progId.length .. progId.length + ver.length] = ver[];
644 		fullBuffer[progId.length + ver.length] = 0;
645 		auto full = fullBuffer.ptr;
646 
647 		// Create ProgID keys
648 		SetKeyAndValue(full, null, "Hello Object");
649 		SetKeyAndValue(full, "CLSID", szID.ptr);
650 
651 		// Create VersionIndependentProgID keys
652 		SetKeyAndValue(partial, null, "Hello Object");
653 		SetKeyAndValue(partial, "CurVer", full);
654 		SetKeyAndValue(partial, "CLSID", szID.ptr);
655 
656 		// Create entries under CLSID
657 		SetKeyAndValue(szCLSID.ptr, null, "Hello Object");
658 		SetKeyAndValue(szCLSID.ptr, "ProgID", full);
659 		SetKeyAndValue(szCLSID.ptr, "VersionIndependentProgID", partial);
660 		SetKeyAndValue(szCLSID.ptr, "NotInsertable", null);
661 
662 		GetModuleFileNameA(g_hInst, szModule.ptr, szModule.length);
663 
664 		SetKeyAndValue(szCLSID.ptr, "InprocServer32", szModule.ptr);
665 		return NOERROR;
666 	}
667 
668 	/*
669 	 * Purpose:
670 	 *  Instructs the server to remove its own registry entries
671 	 *
672 	 * Return Value:
673 	 *  HRESULT         NOERROR if registration successful, error
674 	 *                  otherwise.
675 	 */
676 	pragma(mangle, "DllUnregisterServer")
677 	extern(Windows)
678 	HRESULT DllUnregisterServer() {
679 		char[128] szID;
680 		char[128] szCLSID;
681 		char[256] szTemp;
682 
683 		MessageBoxA(null, "DllUnregisterServer()", null, MB_OK);
684 
685 		// Create some base key strings.
686 		StringFromGUID2(&clsid, cast(LPOLESTR) szID, 128);
687 		unicode2ansi(szID.ptr);
688 		strcpy(szCLSID.ptr, "CLSID\\");
689 		strcat(szCLSID.ptr, szID.ptr);
690 
691 		TmpStr tmp;
692 		tmp.append(progId);
693 		tmp.append("\\CurVer");
694 		RegDeleteKeyA(HKEY_CLASSES_ROOT, tmp.getPtr());
695 		tmp.clear();
696 		tmp.append(progId);
697 		tmp.append("\\CLSID");
698 		RegDeleteKeyA(HKEY_CLASSES_ROOT, tmp.getPtr());
699 		tmp.clear();
700 		tmp.append(progId);
701 		RegDeleteKeyA(HKEY_CLASSES_ROOT, tmp.getPtr());
702 
703 		tmp.clear();
704 		tmp.append(progId);
705 		tmp.append(ver);
706 		tmp.append("\\CLSID");
707 		RegDeleteKeyA(HKEY_CLASSES_ROOT, tmp.getPtr());
708 		tmp.clear();
709 		tmp.append(progId);
710 		tmp.append(ver);
711 		RegDeleteKeyA(HKEY_CLASSES_ROOT, tmp.getPtr());
712 
713 		strcpy(szTemp.ptr, szCLSID.ptr);
714 		strcat(szTemp.ptr, "\\");
715 		strcat(szTemp.ptr, "ProgID");
716 		RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);
717 
718 		strcpy(szTemp.ptr, szCLSID.ptr);
719 		strcat(szTemp.ptr, "\\");
720 		strcat(szTemp.ptr, "VersionIndependentProgID");
721 		RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);
722 
723 		strcpy(szTemp.ptr, szCLSID.ptr);
724 		strcat(szTemp.ptr, "\\");
725 		strcat(szTemp.ptr, "NotInsertable");
726 		RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);
727 
728 		strcpy(szTemp.ptr, szCLSID.ptr);
729 		strcat(szTemp.ptr, "\\");
730 		strcat(szTemp.ptr, "InprocServer32");
731 		RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);
732 
733 		RegDeleteKeyA(HKEY_CLASSES_ROOT, szCLSID.ptr);
734 		return NOERROR;
735 	}
736 }
737 
738 /*
739  * SetKeyAndValue
740  *
741  * Purpose:
742  *  Private helper function for DllRegisterServer that creates
743  *  a key, sets a value, and closes that key.
744  *
745  * Parameters:
746  *  pszKey          LPTSTR to the name of the key
747  *  pszSubkey       LPTSTR ro the name of a subkey
748  *  pszValue        LPTSTR to the value to store
749  *
750  * Return Value:
751  *  BOOL            true if successful, false otherwise.
752  */
753 BOOL SetKeyAndValue(LPCSTR pszKey, LPCSTR pszSubkey, LPCSTR pszValue)
754 {
755     HKEY hKey;
756     char[256] szKey;
757     BOOL result;
758 
759     strcpy(szKey.ptr, pszKey);
760 
761     if (pszSubkey)
762     {
763 	strcat(szKey.ptr, "\\");
764 	strcat(szKey.ptr, pszSubkey);
765     }
766 
767     result = true;
768 
769     if (ERROR_SUCCESS != RegCreateKeyExA(HKEY_CLASSES_ROOT,
770 					  szKey.ptr, 0, null, REG_OPTION_NON_VOLATILE,
771 					  KEY_ALL_ACCESS, null, &hKey, null))
772 	result = false;
773     else
774     {
775 	if (null != pszValue)
776 	{
777 	    if (RegSetValueExA(hKey, null, 0, REG_SZ, cast(BYTE *) pszValue,
778                            cast(uint)((strlen(pszValue) + 1) * char.sizeof)) != ERROR_SUCCESS)
779 		result = false;
780 	}
781 
782 	if (RegCloseKey(hKey) != ERROR_SUCCESS)
783 	    result = false;
784     }
785 
786     if (!result)
787 	MessageBoxA(null, "SetKeyAndValue() failed", null, MB_OK);
788 
789     return result;
790 }
791 
792 void unicode2ansi(char *s)
793 {
794     wchar *w;
795 
796     for (w = cast(wchar *) s; *w; w++)
797 	*s++ = cast(char)*w;
798 
799     *s = 0;
800 }
801 
802 /**************************************
803  * Register/unregister a DLL server.
804  * Input:
805  *      flag    !=0: register
806  *              ==0: unregister
807  * Returns:
808  *      0       success
809  *      !=0     failure
810  */
811 
812 extern (Windows) alias HRESULT function() pfn_t;
813 
814 int dll_regserver(const (char) *dllname, int flag) {
815 	char *fn = flag ? cast(char*) "DllRegisterServer"
816 		: cast(char*) "DllUnregisterServer";
817 	int result = 1;
818 	pfn_t pfn;
819 	HINSTANCE hMod;
820 
821 	if (SUCCEEDED(CoInitialize(null))) {
822 		hMod=LoadLibraryA(dllname);
823 
824 		if (hMod > cast(HINSTANCE) HINSTANCE_ERROR) {
825 			pfn = cast(pfn_t)(GetProcAddress(hMod, fn));
826 
827 			if (pfn && SUCCEEDED((*pfn)()))
828 				result = 0;
829 
830 			CoFreeLibrary(hMod);
831 			CoUninitialize();
832 		}
833 	}
834 
835 	return result;
836 }
837 
838 struct TmpStr {
839 	char[256] buffer;
840 	int length;
841 	void clear() { length = 0; }
842 	char* getPtr() {
843 		buffer[length] = 0;
844 		return buffer.ptr;
845 	}
846 
847 	void append(string s) {
848 		buffer[length .. length + s.length] = s[];
849 		length += s.length;
850 	}
851 }
852 
853 
854 /++
855 
856 module com;
857 
858 import com2;
859 
860 interface Refcounting {
861         void AddRef();
862         void Release();
863 }
864 
865 interface Test : Refcounting {
866         void test();
867 }
868 
869 interface Test2 : Refcounting {
870         void test2();
871 }
872 
873 class Foo : Implements!Test, Implements!Test2 {
874         override void test() {
875                 import std.stdio;
876                 writeln("amazing");
877         }
878 
879         void test2() {}
880 
881         mixin Refcounts;
882 }
883 mixin RegisterComImplementation!(Foo, "some-guid");
884 
885 void main() {
886         auto foo = new Foo();
887         auto c = foo.getComProxy();
888         c.test();
889 
890 }
891 
892 +/
893 
894 /++
895 
896 module com2;
897 
898 /+
899         The COM interface's implementation is done by a
900         generated class, forwarding it to the other D
901         implementation
902 
903         if it implements IDispatch then it can do the dynamic
904         thing too automatically!
905 +/
906 
907 template Implements(Interface) {
908         private static class Helper : Interface {
909                 Implements i;
910                 this(Implements i) {
911                         this.i = i;
912                 }
913 
914                 static foreach(memberName; __traits(allMembers, Interface))
915                 mixin(q{ void } ~ memberName ~ q{ () {
916                         import std.stdio; writeln("wrapper begin");
917                         __traits(getMember, i, memberName)();
918                         writeln("wrapper end");
919                 }});
920         }
921 
922         interface Implements {
923                 final Helper getComProxy() {
924                         return new Helper(this);
925                 }
926 
927                 static foreach(memberName; __traits(allMembers, Interface))
928                 mixin(q{ void } ~ memberName ~ q{ (); });
929 
930                 mixin template Refcounts() {
931                         int refcount;
932                         void AddRef() { refcount ++; }
933                         void Release() { refcount--; }
934                 }
935         }
936 }
937 
938 // the guid may also be a UDA on Class, but you do need to register your implementations
939 mixin template RegisterComImplementation(Class, string guid = null) {
940 
941 }
942 
943 // wraps the interface with D-friendly type and provides RAII for the object
944 struct ComClient(I) {}
945 // eg: alias XmlDocument = ComClient!IXmlDocument;
946 // then you get it through a com factory
947 
948 ComClient!I getCom(T)(string guid) { return ComClient!I(); }
949 
950 +/