1 /* Copyright (c) 2013 Tobias Wolf, All Rights Reserved
2  *
3  * The contents of this file is dual-licensed under 2
4  * alternative Open Source/Free licenses: LGPL 2.1 or later and
5  * Apache License 2.0. (starting with JNA version 4.0.0).
6  *
7  * You can freely decide which license you want to apply to
8  * the project.
9  *
10  * You may obtain a copy of the LGPL License at:
11  *
12  * http://www.gnu.org/licenses/licenses.html
13  *
14  * A copy is also included in the downloadable source code package
15  * containing JNA, in file "LGPL2.1".
16  *
17  * You may obtain a copy of the Apache License at:
18  *
19  * http://www.apache.org/licenses/
20  *
21  * A copy is also included in the downloadable source code package
22  * containing JNA, in file "AL2.0".
23  */
24 package com.sun.jna.platform.win32.COM;
25 
26 import com.sun.jna.LastErrorException;
27 import java.util.ArrayList;
28 
29 import com.sun.jna.Native;
30 import com.sun.jna.Pointer;
31 import com.sun.jna.platform.win32.Advapi32;
32 import com.sun.jna.platform.win32.Advapi32Util;
33 import com.sun.jna.platform.win32.Advapi32Util.EnumKey;
34 import com.sun.jna.platform.win32.Advapi32Util.InfoKey;
35 import com.sun.jna.platform.win32.Kernel32Util;
36 import com.sun.jna.platform.win32.OaIdl.EXCEPINFO;
37 import com.sun.jna.platform.win32.Ole32;
38 import com.sun.jna.platform.win32.OleAuto;
39 import com.sun.jna.platform.win32.W32Errors;
40 import com.sun.jna.platform.win32.WinNT;
41 import com.sun.jna.platform.win32.WinNT.HRESULT;
42 import com.sun.jna.platform.win32.WinReg;
43 import com.sun.jna.platform.win32.WinReg.HKEYByReference;
44 import com.sun.jna.ptr.IntByReference;
45 
46 /**
47  * The Class COMUtils.
48  *
49  * @author wolf.tobias@gmx.net The Class COMUtils.
50  */
51 public abstract class COMUtils {
52 
53     /** The Constant CO_E_NOTINITIALIZED. */
54     public static final int S_OK = 0;
55     public static final int S_FALSE = 1;
56     public static final int E_UNEXPECTED=0x8000FFFF;
57 
58     /**
59      * Succeeded.
60      *
61      * @param hr
62      *            the hr
63      * @return true, if successful
64      */
SUCCEEDED(HRESULT hr)65     public static boolean SUCCEEDED(HRESULT hr) {
66         return SUCCEEDED(hr.intValue());
67     }
68 
69     /**
70      * Succeeded.
71      *
72      * @param hr
73      *            the hr
74      * @return true, if successful
75      */
SUCCEEDED(int hr)76     public static boolean SUCCEEDED(int hr) {
77         return hr >= 0;
78     }
79 
80     /**
81      * Failed.
82      *
83      * @param hr
84      *            the hr
85      * @return true, if successful
86      */
FAILED(HRESULT hr)87     public static boolean FAILED(HRESULT hr) {
88         return FAILED(hr.intValue());
89     }
90 
91     /**
92      * Failed.
93      *
94      * @param hr
95      *            the hr
96      * @return true, if successful
97      */
FAILED(int hr)98     public static boolean FAILED(int hr) {
99         return hr < 0;
100     }
101 
102     /**
103      * Throw new exception.
104      *
105      * @param hr
106      *            the hr
107      */
checkRC(HRESULT hr)108     public static void checkRC(HRESULT hr) {
109         if (FAILED(hr)) {
110             String formatMessage;
111             try {
112                 formatMessage = Kernel32Util.formatMessage(hr) + "(HRESULT: " + Integer.toHexString(hr.intValue()) + ")";
113             } catch (LastErrorException ex) {
114                 // throws if HRESULT can't be resolved
115                 formatMessage = "(HRESULT: " + Integer.toHexString(hr.intValue()) + ")";
116             }
117             throw new COMException(formatMessage, hr);
118         }
119     }
120 
121     /**
122      * Check status of HRESULT if it indicates a failed call a COMInvokeException
123      * is reaised.
124      *
125      * <p>The string members of the pExcepInfo are freed in this call and can't
126      * be used afterwards. The structure is not freeed, as it is expected, that
127      * is allocated via the Memory object of JNA.</p>
128      *
129      * @param hr
130      *            the hr
131      * @param pExcepInfo
132      *            the excep info, it is expected
133      * @param puArgErr
134      *            the pu arg err
135      */
checkRC(HRESULT hr, EXCEPINFO pExcepInfo, IntByReference puArgErr)136     public static void checkRC(HRESULT hr, EXCEPINFO pExcepInfo,
137             IntByReference puArgErr) {
138 
139         COMException resultException = null;
140 
141         if (FAILED(hr)) {
142             StringBuilder formatMessage = new StringBuilder();
143 
144             Integer errorArg = null;
145             Integer wCode = null;
146             Integer scode = null;
147             String description = null;
148             String helpFile = null;
149             Integer helpCtx = null;
150             String source = null;
151 
152             if(puArgErr != null) {
153                 errorArg = puArgErr.getValue();
154             }
155 
156             try {
157                 formatMessage.append(Kernel32Util.formatMessage(hr));
158             } catch (LastErrorException ex) {
159                 // throws if HRESULT can't be resolved
160             }
161 
162             formatMessage.append("(HRESULT: ");
163             formatMessage.append(Integer.toHexString(hr.intValue()));
164             formatMessage.append(")");
165 
166             if(pExcepInfo != null) {
167                 wCode = pExcepInfo.wCode.intValue();
168                 scode = pExcepInfo.scode.intValue();
169                 helpCtx = pExcepInfo.dwHelpContext.intValue();
170 
171                 if(pExcepInfo.bstrSource != null) {
172                     source = pExcepInfo.bstrSource.getValue();
173                     formatMessage.append("\nSource:      ");
174                     formatMessage.append(source);
175                 }
176                 if(pExcepInfo.bstrDescription != null) {
177                     description = pExcepInfo.bstrDescription.getValue();
178                     formatMessage.append("\nDescription: ");
179                     formatMessage.append(description);
180                 }
181                 if(pExcepInfo.bstrHelpFile != null) {
182                     helpFile = pExcepInfo.bstrHelpFile.getValue();
183                 }
184             }
185 
186             throw new COMInvokeException(
187                     formatMessage.toString(),
188                     hr,
189                     errorArg,
190                     description,
191                     helpCtx,
192                     helpFile,
193                     scode,
194                     source,
195                     wCode
196             );
197         }
198 
199         if(pExcepInfo != null) {
200             if(pExcepInfo.bstrSource != null) {
201                 OleAuto.INSTANCE.SysFreeString(pExcepInfo.bstrSource);
202             }
203             if(pExcepInfo.bstrDescription != null) {
204                 OleAuto.INSTANCE.SysFreeString(pExcepInfo.bstrDescription);
205             }
206             if(pExcepInfo.bstrHelpFile != null) {
207                 OleAuto.INSTANCE.SysFreeString(pExcepInfo.bstrHelpFile);
208             }
209         }
210 
211         if(resultException != null) {
212             throw resultException;
213         }
214     }
215 
216     /**
217      * Gets the all com info on system.
218      *
219      * @return the all com info on system
220      */
getAllCOMInfoOnSystem()221     public static ArrayList<COMInfo> getAllCOMInfoOnSystem() {
222         HKEYByReference phkResult = new HKEYByReference();
223         HKEYByReference phkResult2 = new HKEYByReference();
224         String subKey;
225         ArrayList<COMInfo> comInfos = new ArrayList<COMUtils.COMInfo>();
226 
227         try {
228             // open root key
229             phkResult = Advapi32Util.registryGetKey(WinReg.HKEY_CLASSES_ROOT,
230                     "CLSID", WinNT.KEY_READ);
231             // open subkey
232             InfoKey infoKey = Advapi32Util.registryQueryInfoKey(
233                     phkResult.getValue(), WinNT.KEY_READ);
234 
235             for (int i = 0; i < infoKey.lpcSubKeys.getValue(); i++) {
236                 EnumKey enumKey = Advapi32Util.registryRegEnumKey(
237                         phkResult.getValue(), i);
238                 subKey = Native.toString(enumKey.lpName);
239 
240                 COMInfo comInfo = new COMInfo(subKey);
241 
242                 phkResult2 = Advapi32Util.registryGetKey(phkResult.getValue(),
243                         subKey, WinNT.KEY_READ);
244                 InfoKey infoKey2 = Advapi32Util.registryQueryInfoKey(
245                         phkResult2.getValue(), WinNT.KEY_READ);
246 
247                 for (int y = 0; y < infoKey2.lpcSubKeys.getValue(); y++) {
248                     EnumKey enumKey2 = Advapi32Util.registryRegEnumKey(
249                             phkResult2.getValue(), y);
250                     String subKey2 = Native.toString(enumKey2.lpName);
251 
252                     if (subKey2.equals("InprocHandler32")) {
253                         comInfo.inprocHandler32 = (String) Advapi32Util
254                                 .registryGetValue(phkResult2.getValue(),
255                                         subKey2, null);
256                     } else if (subKey2.equals("InprocServer32")) {
257                         comInfo.inprocServer32 = (String) Advapi32Util
258                                 .registryGetValue(phkResult2.getValue(),
259                                         subKey2, null);
260                     } else if (subKey2.equals("LocalServer32")) {
261                         comInfo.localServer32 = (String) Advapi32Util
262                                 .registryGetValue(phkResult2.getValue(),
263                                         subKey2, null);
264                     } else if (subKey2.equals("ProgID")) {
265                         comInfo.progID = (String) Advapi32Util
266                                 .registryGetValue(phkResult2.getValue(),
267                                         subKey2, null);
268                     } else if (subKey2.equals("TypeLib")) {
269                         comInfo.typeLib = (String) Advapi32Util
270                                 .registryGetValue(phkResult2.getValue(),
271                                         subKey2, null);
272                     }
273                 }
274 
275                 Advapi32.INSTANCE.RegCloseKey(phkResult2.getValue());
276                 comInfos.add(comInfo);
277             }
278         } finally {
279             Advapi32.INSTANCE.RegCloseKey(phkResult.getValue());
280             Advapi32.INSTANCE.RegCloseKey(phkResult2.getValue());
281         }
282 
283         return comInfos;
284     }
285 
286     /**
287      * Check if COM was initialized correctly. The initialization status is not changed!
288      *
289      * <p>This is a debug function, not for normal usage!</p>
290      *
291      * @return whether COM has been initialized
292      */
comIsInitialized()293     public static boolean comIsInitialized() {
294         WinNT.HRESULT hr = Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
295         if (hr.equals(W32Errors.S_OK)) {
296             // User failed - uninitialize again and return false
297             Ole32.INSTANCE.CoUninitialize();
298             return false;
299         } else if (hr.equals(W32Errors.S_FALSE)) {
300             // OK Variant 1 - User initialized COM with same threading module as
301             // in this check. According to MSDN CoUninitialize needs to be called
302             // in this case.
303             Ole32.INSTANCE.CoUninitialize();
304             return true;
305         } else if (hr.intValue() == W32Errors.RPC_E_CHANGED_MODE) {
306             return true;
307         }
308         // If another result than the checked ones above happens handling is
309         // delegated to the "normal" COM exception handling and a COMException
310         // will be raised.
311         COMUtils.checkRC(hr);
312         // The return will not be met, as COMUtils#checkRC will raise an exception
313         return false;
314     }
315 
316     /**
317      * The Class COMInfo.
318      *
319      * @author wolf.tobias@gmx.net The Class COMInfo.
320      */
321     public static class COMInfo {
322 
323         /** The clsid. */
324         public String clsid;
325 
326         /** The inproc handler32. */
327         public String inprocHandler32;
328 
329         /** The inproc server32. */
330         public String inprocServer32;
331 
332         /** The local server32. */
333         public String localServer32;
334 
335         /** The prog id. */
336         public String progID;
337 
338         /** The type lib. */
339         public String typeLib;
340 
341         /**
342          * Instantiates a new cOM info.
343          */
COMInfo()344         public COMInfo() {
345         }
346 
347         /**
348          * Instantiates a new cOM info.
349          *
350          * @param clsid
351          *            the clsid
352          */
COMInfo(String clsid)353         public COMInfo(String clsid) {
354             this.clsid = clsid;
355         }
356     }
357 }
358