1 module nulib.system.com.com;
2 import nulib.system.com.objbase;
3 import nulib.system.com.uuid;
4 import numem.core.atomic : nu_atomic_add_32, nu_atomic_sub_32;
5 import numem;
6 
7 public import nulib.system.com.hresult;
8 public import nulib.system.com.unk;
9 import nulib.system.com.objbase;
10 
11 /**
12     Class Context for COM Object Instantiation.
13 */
14 enum ComClassContext : CLSCTX {
15     
16     /**
17         Class Context that covers everything.
18     */
19     all =       CLSCTX.CLSCTX_INPROC_SERVER|CLSCTX.CLSCTX_INPROC_HANDLER|CLSCTX.CLSCTX_LOCAL_SERVER,
20         
21     /**
22         Class Context that covers in-process COM.
23     */
24     inProcess = CLSCTX.CLSCTX_INPROC_SERVER|CLSCTX.CLSCTX_INPROC_HANDLER,
25 
26     /**
27         Class Context that covers remote/serverside COM.
28     */
29     server =    CLSCTX.CLSCTX_INPROC_SERVER|CLSCTX.CLSCTX_LOCAL_SERVER|CLSCTX.CLSCTX_REMOTE_SERVER,
30 }
31 
32 /**
33     Determines the concurrency model used for incoming calls to 
34     objects created by this thread.
35 
36     This concurrency model can be either apartment-threaded or multithreaded.
37 */
38 enum ComInitFlags : uint {
39     
40     /**
41         Initializes the thread for (single) apartment-threaded object concurrency.
42     */
43     appartmentThreaded = 0x2,
44     
45     /**
46         Initializes the thread for multithreaded object concurrency.
47     */
48     multithreaded = 0x0,
49     
50     /**
51         Disables DDE for OLE1 support.
52     */
53     disableOLE1DDE = 0x4,
54     
55     /**
56         Increase memory usage in an attempt to increase performance.
57     */
58     speedOverMemory = 0x8,
59 }
60 alias COINIT = ComInitFlags; /// ditto
61 
62 /**
63     Initializes the COM Framework.
64     
65     Returns:
66         $(D true) if the COM framework was initialized,
67         $(D false) otherwise.
68 */
69 extern(C)
70 bool nu_com_init(ComInitFlags flags = ComInitFlags.appartmentThreaded) {
71     return CoInitializeEx(flags, null) == S_OK;
72 }
73 
74 /**
75     Closes the COM Framework.
76 */
77 extern(C)
78 void nu_com_quit() {
79     CoUninitialize();
80 }
81 
82 /**
83     Gets the current COM Thread ID. 
84 */
85 extern(C)
86 uint nu_com_gettid() {
87     return CoGetCurrentProcess();
88 }
89 
90 /**
91     Frees specified library or unused libraries if $(D lib) is $(D null)
92 
93     Params:
94         lib = the library to free, or $(D null) to free unused.
95 */
96 extern(C)
97 void nu_com_freelibrary(void* lib) {
98     if (lib)
99         CoFreeLibrary(lib);
100     else
101         CoFreeUnusedLibraries();
102 }
103 
104 /**
105     Base COM Class for D extensions to the COM API.
106 */
107 @nu_autoreleasewith!((ref obj) { obj.Release(); })
108 class ComObject : IUnknown {
109 extern(Windows) @nogc:
110 private:
111     uint refcount;
112 
113 public:
114 
115     /**
116         Creates and default-initializes a single object of the class specified.
117 
118         Params:
119             target =    The target interface to put the class instance into.
120             context =   The context in which the class will be instantiated.
121             outer =     The outer class instance that this instance should be instantiated
122                         within, or $(D null) if it's not an aggregate COM Object.
123 
124         Returns:
125             $(D HRESULT) describing whether the operation succeeded.
126             Check against $(D S_OK).
127     */
128     extern(D)
129     static HRESULT createInstance(T, I = IUnknown)(ref I target, CLSCTX context = CLSCTX_INPROC, IUnknown outer = null) 
130     if (is(T == class) && is(I == interface) && is(T : IUnknown) && is(I : IUnknown)) {
131         const(Guid) clsid = __uuidof!T;
132         const(Guid) riid = __uuidof!I;
133         return CoCreateInstance(&clsid, outer, context, &riid, cast(void**)&target);
134     }
135 
136     /**
137         Params:
138             progID =    ProgID of the class to instantiate in any encoding.
139                         The encoding is automatically converted to the correct format.
140             target =    The target instance to create
141             context =   The class context
142             outer =     The outer class
143     */
144     extern(D)
145     static HRESULT createInstance(T, I = IUnknown)(inout(T)[] progID, ref I target, ComClassContext context = ComClassContext.inProcess, IUnknown outer = null) 
146     if (is(I == interface) && is(I : IUnknown)) {
147         import nulib.string : nwstring;
148 
149         nwstring clsname = progID;
150         const(Guid) riid = __uuidof!I;
151         const(Guid) clsid;
152         
153         CLSIDFromProgID(clsname.ptr, &clsid);
154         return CoCreateInstance(&clsid, outer, context, &riid, cast(void**)&target);
155     }
156 
157     /**
158         Queries a COM object for a pointer to one of its interface.
159 
160         Note:
161             Because you pass the address of an interface pointer, 
162             the method can overwrite that address with the pointer to the interface being queried for.
163             Upon successful return, $(D *ppvObject) (the dereferenced address) contains a pointer to 
164             the requested interface.
165             If the object doesn't support the interface, the method sets $(D *ppvObject) 
166             (the dereferenced address) to $(D null).
167 
168         Params:
169             riid = Pointer to the Guid of the interface being queried for.
170             ppvObject = Pointer to pointer to store result of operation.
171 
172         Returns:
173             $(D S_OK) if supported, $(D E_NOINTERFACE) otherwise.
174             If ppvObject is $(D null), returns $(D E_POINTER). 
175     */
176     HRESULT QueryInterface(const(IID)* riid, out void* ppvObject) { // @suppress(dscanner.style.phobos_naming_convention)
177         if (*riid == IUnknown.iid) {
178             ppvObject = cast(void*)cast(IUnknown)this;
179             AddRef();
180             return S_OK;
181         }
182 
183         ppvObject = null;
184         return E_NOINTERFACE;
185     }
186 
187     /**
188         Increments the reference count for an interface pointer to a COM object.
189 
190         You should call this method whenever you make a copy of an interface pointer.
191 
192         Returns:
193             The new reference count, should only be used for debugging.
194     */
195     uint AddRef() { // @suppress(dscanner.style.phobos_naming_convention)
196         return nu_atomic_add_32(refcount, 1);
197     }
198     
199     /**
200         Decrements the reference count for an interface on a COM object.
201 
202         Returns:
203             The new reference count, should only be used for debugging.
204     */
205     uint Release() { // @suppress(dscanner.style.phobos_naming_convention)
206         int lref = nu_atomic_sub_32(refcount, 1);
207         if (lref == 0) {
208             ComObject self = this;
209             nogc_delete(self);
210         }
211         return cast(uint)lref;
212     }
213 }