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