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 +/