/// <summary> /// Constructor /// </summary> /// <param name="info">ITypeInfo object being wrapped by this object</param> internal ComTypeInfo(COM.ITypeInfo info) { _typeinfo = info; _properties = new Dictionary<String, ComProperty>(StringComparer.OrdinalIgnoreCase); _methods = new Dictionary<String, ComMethod>(StringComparer.OrdinalIgnoreCase); if (_typeinfo != null) { Initialize(); } }
/// <summary> /// creates an entity support list for a proxy /// </summary> /// <param name="comProxy"></param> /// <returns></returns> internal static Dictionary <string, string> GetSupportedEntities(object comProxy) { Guid parentLibraryGuid = GetParentLibraryGuid(comProxy); string className = TypeDescriptor.GetClassName(comProxy); string key = (parentLibraryGuid.ToString() + className).ToLower(); Dictionary <string, string> supportList = null; if (_entitiesListCache.TryGetValue(key, out supportList)) { return(supportList); } supportList = new Dictionary <string, string>(); IDispatch dispatch = comProxy as IDispatch; if (null == dispatch) { throw new COMException("Unable to cast underlying proxy to IDispatch."); } COMTypes.ITypeInfo typeInfo = dispatch.GetTypeInfo(0, 0); if (null == typeInfo) { throw new COMException("GetTypeInfo returns null."); } IntPtr typeAttrPointer = IntPtr.Zero; typeInfo.GetTypeAttr(out typeAttrPointer); COMTypes.TYPEATTR typeAttr = (COMTypes.TYPEATTR)Marshal.PtrToStructure(typeAttrPointer, typeof(COMTypes.TYPEATTR)); for (int i = 0; i < typeAttr.cFuncs; i++) { string strName, strDocString, strHelpFile; int dwHelpContext; IntPtr funcDescPointer = IntPtr.Zero; System.Runtime.InteropServices.ComTypes.FUNCDESC funcDesc; typeInfo.GetFuncDesc(i, out funcDescPointer); funcDesc = (COMTypes.FUNCDESC)Marshal.PtrToStructure(funcDescPointer, typeof(System.Runtime.InteropServices.ComTypes.FUNCDESC)); switch (funcDesc.invkind) { case System.Runtime.InteropServices.ComTypes.INVOKEKIND.INVOKE_PROPERTYGET: case System.Runtime.InteropServices.ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT: case System.Runtime.InteropServices.ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF: { typeInfo.GetDocumentation(funcDesc.memid, out strName, out strDocString, out dwHelpContext, out strHelpFile); string outValue = ""; bool exists = supportList.TryGetValue("Property-" + strName, out outValue); if (!exists) { supportList.Add("Property-" + strName, strDocString); } break; } case System.Runtime.InteropServices.ComTypes.INVOKEKIND.INVOKE_FUNC: { typeInfo.GetDocumentation(funcDesc.memid, out strName, out strDocString, out dwHelpContext, out strHelpFile); string outValue = ""; bool exists = supportList.TryGetValue("Method-" + strName, out outValue); if (!exists) { supportList.Add("Method-" + strName, strDocString); } break; } } typeInfo.ReleaseFuncDesc(funcDescPointer); } typeInfo.ReleaseTypeAttr(typeAttrPointer); Marshal.ReleaseComObject(typeInfo); _entitiesListCache.Add(key, supportList); return(supportList); }
/// <summary> /// Initializes a new instance of ComProperty. /// </summary> /// <param name="typeinfo">Reference to the ITypeInfo of the COM object.</param> /// <param name="name">Name of the property being created.</param> internal ComProperty(COM.ITypeInfo typeinfo, string name) { _typeInfo = typeinfo; Name = name; }
// Disable obsolete warning about VarEnum in CoreCLR #pragma warning disable 618 /// <summary> /// This function gets a string representation of the Type Descriptor /// This is used in generating signature for Properties and Methods. /// </summary> /// <param name="typeinfo">Reference to the type info to which the type descriptor belongs.</param> /// <param name="typedesc">Reference to type descriptor which is being converted to string from.</param> /// <returns>String representation of the type descriptor.</returns> private static string GetStringFromTypeDesc(COM.ITypeInfo typeinfo, COM.TYPEDESC typedesc) { if ((VarEnum)typedesc.vt == VarEnum.VT_PTR) { COM.TYPEDESC refdesc = Marshal.PtrToStructure <COM.TYPEDESC>(typedesc.lpValue); return(GetStringFromTypeDesc(typeinfo, refdesc)); } if ((VarEnum)typedesc.vt == VarEnum.VT_SAFEARRAY) { COM.TYPEDESC refdesc = Marshal.PtrToStructure <COM.TYPEDESC>(typedesc.lpValue); return("SAFEARRAY(" + GetStringFromTypeDesc(typeinfo, refdesc) + ")"); } if ((VarEnum)typedesc.vt == VarEnum.VT_USERDEFINED) { return(GetStringFromCustomType(typeinfo, typedesc.lpValue)); } switch ((VarEnum)typedesc.vt) { case VarEnum.VT_I1: return("char"); case VarEnum.VT_I2: return("short"); case VarEnum.VT_I4: case VarEnum.VT_INT: case VarEnum.VT_HRESULT: return("int"); case VarEnum.VT_I8: return("int64"); case VarEnum.VT_R4: return("float"); case VarEnum.VT_R8: return("double"); case VarEnum.VT_UI1: return("byte"); case VarEnum.VT_UI2: return("ushort"); case VarEnum.VT_UI4: case VarEnum.VT_UINT: return("uint"); case VarEnum.VT_UI8: return("uint64"); case VarEnum.VT_BSTR: case VarEnum.VT_LPSTR: case VarEnum.VT_LPWSTR: return("string"); case VarEnum.VT_DATE: return("Date"); case VarEnum.VT_BOOL: return("bool"); case VarEnum.VT_CY: return("currency"); case VarEnum.VT_DECIMAL: return("decimal"); case VarEnum.VT_CLSID: return("clsid"); case VarEnum.VT_DISPATCH: return("IDispatch"); case VarEnum.VT_UNKNOWN: return("IUnknown"); case VarEnum.VT_VARIANT: return("Variant"); case VarEnum.VT_VOID: return("void"); case VarEnum.VT_ARRAY: return("object[]"); case VarEnum.VT_EMPTY: return(string.Empty); default: return("Unknown!"); } }
/// <summary> /// Gets Method Signature from FuncDesc describing the method. /// </summary> /// <param name="typeinfo">ITypeInfo interface of the object.</param> /// <param name="funcdesc">FuncDesc which defines the method.</param> /// <param name="isPropertyPut">True if this is a property put; these properties take their return type from their first parameter.</param> /// <returns>Signature of the method.</returns> internal static string GetMethodSignatureFromFuncDesc(COM.ITypeInfo typeinfo, COM.FUNCDESC funcdesc, bool isPropertyPut) { StringBuilder builder = new StringBuilder(); // First value is function name int namesCount = funcdesc.cParams + 1; string[] names = new string[funcdesc.cParams + 1]; typeinfo.GetNames(funcdesc.memid, names, namesCount, out namesCount); if (!isPropertyPut) { // First get the string for return type. string retstring = GetStringFromTypeDesc(typeinfo, funcdesc.elemdescFunc.tdesc); builder.Append(retstring + " "); } // Append the function name builder.Append(names[0]); builder.Append(" ("); IntPtr ElementDescriptionArrayPtr = funcdesc.lprgelemdescParam; int ElementDescriptionSize = Marshal.SizeOf <COM.ELEMDESC>(); for (int i = 0; i < funcdesc.cParams; i++) { COM.ELEMDESC ElementDescription; int ElementDescriptionArrayByteOffset; IntPtr ElementDescriptionPointer; ElementDescription = new COM.ELEMDESC(); ElementDescriptionArrayByteOffset = i * ElementDescriptionSize; // Disable PRefast warning for converting to int32 and converting back into intptr. // Code below takes into account 32 bit vs 64 bit conversions #pragma warning disable 56515 if (IntPtr.Size == 4) { ElementDescriptionPointer = (IntPtr)(ElementDescriptionArrayPtr.ToInt32() + ElementDescriptionArrayByteOffset); } else { ElementDescriptionPointer = (IntPtr)(ElementDescriptionArrayPtr.ToInt64() + ElementDescriptionArrayByteOffset); } #pragma warning restore 56515 ElementDescription = Marshal.PtrToStructure <COM.ELEMDESC>(ElementDescriptionPointer); string paramstring = GetStringFromTypeDesc(typeinfo, ElementDescription.tdesc); if (i == 0 && isPropertyPut) // use the type of the first argument as the return type { builder.Insert(0, paramstring + " "); } else { builder.Append(paramstring); builder.Append(" " + names[i + 1]); if (i < funcdesc.cParams - 1) { builder.Append(", "); } } } builder.Append(")"); return(builder.ToString()); }
/// <summary> /// Returns a string value representing the type name of the specified COM object. /// </summary> /// <param name="comObj">A COM object the type name of which to return.</param> /// <returns>A string containing the type name.</returns> public static string GetTypeName(object comObj) { if (comObj == null) { return(String.Empty); } if (!Marshal.IsComObject(comObj)) { //The specified object is not a COM object return(String.Empty); } IDispatch dispatch = comObj as IDispatch; if (dispatch == null) { //The specified COM object doesn't support getting type information return(String.Empty); } ComTypes.ITypeInfo typeInfo = null; try { try { // obtain the ITypeInfo interface from the object dispatch.GetTypeInfo(0, 0, out typeInfo); } catch (Exception ex) { //Cannot get the ITypeInfo interface for the specified COM object return(String.Empty); } string typeName = ""; string documentation, helpFile; int helpContext = -1; try { //retrieves the documentation string for the specified type description typeInfo.GetDocumentation(-1, out typeName, out documentation, out helpContext, out helpFile); } catch (Exception ex) { // Cannot extract ITypeInfo information return(String.Empty); } return(typeName); } catch (Exception ex) { // Unexpected error return(String.Empty); } finally { if (typeInfo != null) { Marshal.ReleaseComObject(typeInfo); } } }
private void EnsureScanDefinedMethods() { if (_comTypeDesc?.Funcs != null) { return; } ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(DispatchObject); if (typeInfo == null) { _comTypeDesc = ComTypeDesc.CreateEmptyTypeDesc(); return; } ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); if (_comTypeDesc == null) { lock (s_cacheComTypeDesc) { if (s_cacheComTypeDesc.TryGetValue(typeAttr.guid, out _comTypeDesc) && _comTypeDesc.Funcs != null) { return; } } } ComTypeDesc typeDesc = ComTypeDesc.FromITypeInfo(typeInfo, typeAttr); ComMethodDesc getItem = null; ComMethodDesc setItem = null; Hashtable funcs = new Hashtable(typeAttr.cFuncs); Hashtable puts = new Hashtable(); Hashtable putrefs = new Hashtable(); for (int definedFuncIndex = 0; definedFuncIndex < typeAttr.cFuncs; definedFuncIndex++) { IntPtr funcDescHandleToRelease = IntPtr.Zero; try { GetFuncDescForDescIndex(typeInfo, definedFuncIndex, out ComTypes.FUNCDESC funcDesc, out funcDescHandleToRelease); if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FRESTRICTED) != 0) { // This function is not meant for the script user to use. continue; } ComMethodDesc method = new ComMethodDesc(typeInfo, funcDesc); string name = method.Name.ToUpper(CultureInfo.InvariantCulture); if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT) != 0) { puts.Add(name, method); // for the special dispId == 0, we need to store // the method descriptor for the Do(SetItem) binder. if (method.DispId == ComDispIds.DISPID_VALUE && setItem == null) { setItem = method; } continue; } if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF) != 0) { putrefs.Add(name, method); // for the special dispId == 0, we need to store // the method descriptor for the Do(SetItem) binder. if (method.DispId == ComDispIds.DISPID_VALUE && setItem == null) { setItem = method; } continue; } if (funcDesc.memid == ComDispIds.DISPID_NEWENUM) { funcs.Add("GETENUMERATOR", method); continue; } funcs.Add(name, method); // for the special dispId == 0, we need to store the method descriptor // for the Do(GetItem) binder. if (funcDesc.memid == ComDispIds.DISPID_VALUE) { getItem = method; } } finally { if (funcDescHandleToRelease != IntPtr.Zero) { typeInfo.ReleaseFuncDesc(funcDescHandleToRelease); } } } lock (s_cacheComTypeDesc) { if (s_cacheComTypeDesc.TryGetValue(typeAttr.guid, out ComTypeDesc cachedTypeDesc)) { _comTypeDesc = cachedTypeDesc; } else { _comTypeDesc = typeDesc; s_cacheComTypeDesc.Add(typeAttr.guid, _comTypeDesc); } _comTypeDesc.Funcs = funcs; _comTypeDesc.Puts = puts; _comTypeDesc.PutRefs = putrefs; _comTypeDesc.EnsureGetItem(getItem); _comTypeDesc.EnsureSetItem(setItem); } }
private void EnsureScanDefinedMethods() { if (_comTypeDesc != null && _comTypeDesc.Funcs != null) { return; } ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(DispatchObject, true); if (typeInfo == null) { _comTypeDesc = ComTypeDesc.CreateEmptyTypeDesc(); return; } ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); if (_comTypeDesc == null) { lock (s_cacheComTypeDesc) { if (s_cacheComTypeDesc.TryGetValue(typeAttr.guid, out _comTypeDesc) == true && _comTypeDesc.Funcs != null) { return; } } } if (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_INTERFACE) { //We have typeinfo for custom interface. Get typeinfo for Dispatch interface. typeInfo = ComTypeInfo.GetDispatchTypeInfoFromCustomInterfaceTypeInfo(typeInfo); typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); } if (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_COCLASS) { //We have typeinfo for the COClass. Find the default interface and get typeinfo for default interface. typeInfo = ComTypeInfo.GetDispatchTypeInfoFromCoClassTypeInfo(typeInfo); typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); } ComTypeDesc typeDesc = ComTypeDesc.FromITypeInfo(typeInfo, typeAttr); ComMethodDesc getItem = null; ComMethodDesc setItem = null; Hashtable funcs = new Hashtable(typeAttr.cFuncs); Hashtable puts = new Hashtable(); Hashtable putrefs = new Hashtable(); for (int definedFuncIndex = 0; definedFuncIndex < typeAttr.cFuncs; definedFuncIndex++) { IntPtr funcDescHandleToRelease = IntPtr.Zero; try { ComTypes.FUNCDESC funcDesc; GetFuncDescForDescIndex(typeInfo, definedFuncIndex, out funcDesc, out funcDescHandleToRelease); if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FRESTRICTED) != 0) { // This function is not meant for the script user to use. continue; } ComMethodDesc method = new ComMethodDesc(typeInfo, funcDesc); string name = method.Name.ToUpper(System.Globalization.CultureInfo.InvariantCulture); if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT) != 0) { // If there is a getter for this put, use that ReturnType as the // PropertyType. if (funcs.ContainsKey(name)) { method.InputType = ((ComMethodDesc)funcs[name]).ReturnType; } puts.Add(name, method); // for the special dispId == 0, we need to store // the method descriptor for the Do(SetItem) binder. if (method.DispId == ComDispIds.DISPID_VALUE && setItem == null) { setItem = method; } continue; } if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF) != 0) { // If there is a getter for this put, use that ReturnType as the // PropertyType. if (funcs.ContainsKey(name)) { method.InputType = ((ComMethodDesc)funcs[name]).ReturnType; } putrefs.Add(name, method); // for the special dispId == 0, we need to store // the method descriptor for the Do(SetItem) binder. if (method.DispId == ComDispIds.DISPID_VALUE && setItem == null) { setItem = method; } continue; } if (funcDesc.memid == ComDispIds.DISPID_NEWENUM) { funcs.Add("GETENUMERATOR", method); continue; } // If there is a setter for this put, update the InputType from our // ReturnType. if (puts.ContainsKey(name)) { ((ComMethodDesc)puts[name]).InputType = method.ReturnType; } if (putrefs.ContainsKey(name)) { ((ComMethodDesc)putrefs[name]).InputType = method.ReturnType; } funcs.Add(name, method); // for the special dispId == 0, we need to store the method descriptor // for the Do(GetItem) binder. if (funcDesc.memid == ComDispIds.DISPID_VALUE) { getItem = method; } } finally { if (funcDescHandleToRelease != IntPtr.Zero) { typeInfo.ReleaseFuncDesc(funcDescHandleToRelease); } } } lock (s_cacheComTypeDesc) { ComTypeDesc cachedTypeDesc; if (s_cacheComTypeDesc.TryGetValue(typeAttr.guid, out cachedTypeDesc)) { _comTypeDesc = cachedTypeDesc; } else { _comTypeDesc = typeDesc; s_cacheComTypeDesc.Add(typeAttr.guid, _comTypeDesc); } _comTypeDesc.Funcs = funcs; _comTypeDesc.Puts = puts; _comTypeDesc.PutRefs = putrefs; _comTypeDesc.EnsureGetItem(getItem); _comTypeDesc.EnsureSetItem(setItem); } }
public TypeInfoVariablesCollection(ComTypes.ITypeInfo parent, ComTypes.TYPEATTR attributes) { Parent = parent; _count = attributes.cVars; }
internal ComTypeEnumDesc(ComTypes.ITypeInfo typeInfo) : base(typeInfo) { }
/// <summary> /// Initializes new instance of ComMethod class. /// </summary> internal ComMethod(COM.ITypeInfo typeinfo, string name) { _typeInfo = typeinfo; Name = name; }
internal static string GetNameOfMethod(ComTypes.ITypeInfo typeInfo, int memid) { string[] rgNames = new string[1]; typeInfo.GetNames(memid, rgNames, 1, out int _); return(rgNames[0]); }
internal static void GetInfoFromType(ComTypes.ITypeInfo typeInfo, out string name, out string documentation) { typeInfo.GetDocumentation(-1, out name, out documentation, out int _, out string _); }
internal static string GetNameOfType(ComTypes.ITypeInfo typeInfo) { GetInfoFromType(typeInfo, out string name, out string _); return(name); }
/// <summary> /// Initializes a new instance of ComProperty. /// </summary> /// <param name="typeinfo">reference to the ITypeInfo of the COM object</param> /// <param name="name">name of the property being created.</param> internal ComProperty(COM.ITypeInfo typeinfo, string name) { _typeInfo = typeinfo; Name = name; }
private void EnsureScanDefinedEvents() { // _comTypeDesc.Events is null if we have not yet attempted // to scan the object for events. if (_comTypeDesc?.Events != null) { return; } // check type info in the type descriptions cache ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(DispatchObject); if (typeInfo == null) { _comTypeDesc = ComTypeDesc.CreateEmptyTypeDesc(); return; } ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); if (_comTypeDesc == null) { lock (s_cacheComTypeDesc) { if (s_cacheComTypeDesc.TryGetValue(typeAttr.guid, out _comTypeDesc) && _comTypeDesc.Events != null) { return; } } } ComTypeDesc typeDesc = ComTypeDesc.FromITypeInfo(typeInfo, typeAttr); ComTypes.ITypeInfo classTypeInfo; Dictionary <string, ComEventDesc> events; var cpc = RuntimeCallableWrapper as ComTypes.IConnectionPointContainer; if (cpc == null) { // No ICPC - this object does not support events events = ComTypeDesc.EmptyEvents; } else if ((classTypeInfo = GetCoClassTypeInfo(RuntimeCallableWrapper, typeInfo)) == null) { // no class info found - this object may support events // but we could not discover those events = ComTypeDesc.EmptyEvents; } else { events = new Dictionary <string, ComEventDesc>(); ComTypes.TYPEATTR classTypeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(classTypeInfo); for (int i = 0; i < classTypeAttr.cImplTypes; i++) { classTypeInfo.GetRefTypeOfImplType(i, out int hRefType); classTypeInfo.GetRefTypeInfo(hRefType, out ComTypes.ITypeInfo interfaceTypeInfo); classTypeInfo.GetImplTypeFlags(i, out ComTypes.IMPLTYPEFLAGS flags); if ((flags & ComTypes.IMPLTYPEFLAGS.IMPLTYPEFLAG_FSOURCE) != 0) { ScanSourceInterface(interfaceTypeInfo, ref events); } } if (events.Count == 0) { events = ComTypeDesc.EmptyEvents; } } lock (s_cacheComTypeDesc) { if (s_cacheComTypeDesc.TryGetValue(typeAttr.guid, out ComTypeDesc cachedTypeDesc)) { _comTypeDesc = cachedTypeDesc; } else { _comTypeDesc = typeDesc; s_cacheComTypeDesc.Add(typeAttr.guid, _comTypeDesc); } _comTypeDesc.Events = events; } }
/// <summary> /// Returns all running com proxies + add. informations from the running object table there matched with the input parameters /// WARNING: the method returns always the first com proxy from the running object table if multiple (match) proxies exists. /// </summary> /// <returns>IDisposableEnumeration with proxy informations</returns> public static IDisposableEnumeration <ProxyInformation> GetActiveProxyInformations() { IEnumMoniker monikerList = null; IRunningObjectTable runningObjectTable = null; RunningObjectTableItemCollection resultList = new RunningObjectTableItemCollection(); try { // query table and returns null if no objects running if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null) { return(null); } // query moniker & reset runningObjectTable.EnumRunning(out monikerList); monikerList.Reset(); IMoniker[] monikerContainer = new IMoniker[1]; IntPtr pointerFetchedMonikers = IntPtr.Zero; // fetch all moniker while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0) { // query com proxy info object comInstance = null; runningObjectTable.GetObject(monikerContainer[0], out comInstance); if (null == comInstance) { continue; } string name = TypeDescriptor.GetClassName(comInstance); string component = TypeDescriptor.GetComponentName(comInstance, false); IBindCtx bindInfo = null; string displayName = String.Empty; Guid classID = Guid.Empty; if (CreateBindCtx(0, out bindInfo) == 0) { monikerContainer[0].GetDisplayName(bindInfo, null, out displayName); monikerContainer[0].GetClassID(out classID); Marshal.ReleaseComObject(bindInfo); } string itemClassName = TypeDescriptor.GetClassName(comInstance); string itemComponentName = TypeDescriptor.GetComponentName(comInstance); COMTypes.ITypeInfo typeInfo = null; string itemLibrary = String.Empty; if (classID != Guid.Empty) { typeInfo = TryCreateTypeInfo(comInstance); itemLibrary = null != typeInfo?GetParentLibraryGuid(typeInfo).ToString() : String.Empty; } string itemID = classID != Guid.Empty ? classID.ToString() : String.Empty; ProxyInformation entry = new ProxyInformation(comInstance, displayName, itemID, itemClassName, itemComponentName, itemLibrary, IntPtr.Zero, ProxyInformation.ProcessElevation.Unknown); resultList.Add(entry); if (classID != Guid.Empty && typeInfo != null) { ReleaseTypeInfo(typeInfo); } } return(resultList); } catch (Exception exception) { DebugConsole.Default.WriteException(exception); throw; } finally { // release proxies if (runningObjectTable != null) { Marshal.ReleaseComObject(runningObjectTable); } if (monikerList != null) { Marshal.ReleaseComObject(monikerList); } } }