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