RightCoder
10/29/2007 3:14:00 PM
Yes, I really think this would be a great article at Code Project. This is an
area that most of us have little or no experience with, I can say that
because I cant find much information about it on the Internet. Even MSDN have
litle documentation on topics like this.
I hope you'll share your code and experience with the rest of us either by
your own homepage/blog or Code Project. What would be great if you could make
a component/class that had some methods I could use that implemented all the
magic you are doing, but that I, as a user, really dont have to know anything
about. You could even earn some money on it ;-)
I'll try to learn some COM-programming the next few weeks and see how far
I'll come. Thanks anyway, Jason.
"Jason Newell" wrote:
> If there is an easier way to do it with existing .NET API's, then I
> guess I've wasted a considerable amount of time developing a lot of
> code. I always look for existing .NET API's before I dive off doing
> things myself, but in this case, I found no other solution.
>
> Fortunately for me, I do have a lot of COM experience so I'm able to
> write the code. The API that I've written closely resembles the
> existing System.Reflection namespace. Everything that I've done starts
> with the name Com. i.e. ComType, ComObject, ComMethodInfo,
> ComPropertyInfo, ComParameterInfo, etc.
>
> The code that I posted is still very much a work in progress. It sounds
> like it could be useful to others so I'll post a copy of the source onto
> my website. Maybe even write a Code Project article about it.
>
> Jason Newell
> www.jasonnewell.net
>
> RightCoder wrote:
> > Hi Jason
> >
> > Thank you for your response. I must admit I have not had a closer look at
> > your code because it seems to be a lot of work to be done before I'm there. I
> > have no experience with COM nor working with IDispatch so this looks like a
> > mountain for me. I dont have the time either to sit down and understand all
> > the details.
> >
> > I may be asking for much here, but I'm looking for a complete solution and I
> > was hoping it was a matter of changing an input-parameter or doing another
> > methodcall. Is it really that difficult to introspect COM from .NET?
> >
> > What else could be done to make this work without taking so long time? Could
> > I make the COMs TLB available on the client? There must be an easier way or a
> > workaround that is doable for a mere mortal like me (ASP.NET developer)? ;-)
> >
> > "Jason Newell" wrote:
> >
> >> The reason that it is taking so long is because it's generating
> >> in-memory interop assemblies behind the scenes. It's basically the same
> >> thing as adding a reference to a type library in Visual Studio although
> >> it seems to be slower than when you manually add the reference.
> >>
> >> The faster but more difficult way to work with COM in .NET is to work
> >> directly with the IDispatch interface. Here is a bit of code that I use
> >> in one of my applications. The code listed below are 3 separate .cs
> >> files. I also did not list my full source so not all functionality is
> >> available in what I'm giving you but it should be enough to give you an
> >> idea as to what's involved.
> >>
> >> Once you have the code in place, you can use it something like:
> >>
> >> Jason Newell
> >> www.jasonnewell.net
> >>
> >>
> >> /* Example Usage */
> >> ComObject comObject = new
> >> ComObject(Marshal.GetActiveObject("Word.Application"));
> >> string[] propertyNames = comObject.GetPropertyNames();
> >> foreach (string propertyName in propertyNames)
> >> {
> >> object property =
> >> comObject.WrappedComObject.GetType().InvokeMember(propertyName,
> >> System.Reflection.BindingFlags.GetProperty, null,
> >> comObject.WrappedComObject, null);
> >> }
> >>
> >>
> >>
> >> /* IDispatch.cs */
> >> using System;
> >> using System.Collections.Generic;
> >> using System.Runtime.InteropServices;
> >> using System.Text;
> >>
> >> namespace SolidEdgeSpy.InteropServices
> >> {
> >> [Guid("00020400-0000-0000-c000-000000000046"),
> >> InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
> >> public interface IDispatch
> >> {
> >> int GetTypeInfoCount();
> >> System.Runtime.InteropServices.ComTypes.ITypeInfo
> >> GetTypeInfo([MarshalAs(UnmanagedType.U4)] int iTInfo,
> >> [MarshalAs(UnmanagedType.U4)] int lcid);
> >> [PreserveSig]
> >> int GetIDsOfNames(ref Guid riid,
> >> [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
> >> string[] rgsNames, int cNames, int lcid,
> >> [MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
> >> [PreserveSig]
> >> int Invoke(int dispIdMember, ref Guid riid,
> >> [MarshalAs(UnmanagedType.U4)] int lcid, [MarshalAs(UnmanagedType.U4)]
> >> int dwFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS
> >> pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[]
> >> pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO
> >> pExcepInfo, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
> >> }
> >> }
> >>
> >> /* ComObject.cs */
> >> using System;
> >> using System.Collections.Generic;
> >> using System.Runtime.InteropServices;
> >> using System.Runtime.InteropServices.ComTypes;
> >> using System.Text;
> >>
> >> namespace SolidEdgeSpy.InteropServices
> >> {
> >> public class ComObject : IDisposable
> >> {
> >> private object _object;
> >> private IDispatch _dispatch;
> >> private IntPtr _pTypeAttr = IntPtr.Zero;
> >> private System.Runtime.InteropServices.ComTypes.ITypeInfo
> >> _typeInfo;
> >> private string _typeName, _typeDescription, _typeHelpFile;
> >> private int _typeHelpContext;
> >> private SolidEdgeSpy.InteropServices.ComTypeLibrary
> >> _comTypeLibrary;
> >>
> >> public ComObject(object comObject)
> >> {
> >> System.Runtime.InteropServices.ComTypes.ITypeLib ppTLB = null;
> >> int pIndex = 0;
> >>
> >> _dispatch = comObject as IDispatch;
> >>
> >> if (_dispatch != null)
> >> {
> >> _object = comObject;
> >> _typeInfo = _dispatch.GetTypeInfo(0, 0);
> >> _typeInfo.GetTypeAttr(out _pTypeAttr);
> >> _typeInfo.GetDocumentation(-1, out _typeName, out
> >> _typeDescription, out _typeHelpContext, out _typeHelpFile);
> >> _typeInfo.GetContainingTypeLib(out ppTLB, out pIndex);
> >> _comTypeLibrary = new ComTypeLibrary(ppTLB);
> >> }
> >> else
> >> {
> >> throw new InvalidComObjectException();
> >> }
> >> }
> >>
> >> ~ComObject()
> >> {
> >> Dispose();
> >> }
> >>
> >> public void Dispose()
> >> {
> >> try
> >> {
> >> if (_typeInfo != null)
> >> {
> >> _typeInfo.ReleaseTypeAttr(_pTypeAttr);
> >> }
> >>
> >> if (_object != null) {
> >> Marshal.ReleaseComObject(_object); _object = null; }
> >> if (_dispatch != null) {
> >> Marshal.ReleaseComObject(_dispatch); _dispatch = null; }
> >> }
> >> catch
> >> {
> >> }
> >> }
> >>
> >> public string[] GetPropertyNames()
> >> {
> >> System.Collections.ArrayList list = new
> >> System.Collections.ArrayList();
> >>
> >> try
> >> {
> >> for (int i = 0; i < this.TypeAttr.cFuncs; i++)
> >> {
> >> IntPtr pFuncDesc = IntPtr.Zero;
> >> System.Runtime.InteropServices.ComTypes.FUNCDESC
> >> funcDesc;
> >> string strName, strDocString, strHelpFile;
> >> int dwHelpContext;
> >>
> >> _typeInfo.GetFuncDesc(i, out pFuncDesc);
> >> funcDesc =
> >> (System.Runtime.InteropServices.ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc,
> >> typeof(System.Runtime.InteropServices.ComTypes.FUNCDESC));
> >>
> >> switch (funcDesc.invkind)
> >> {
> >> case
> >> System.Runtime.InteropServices.ComTypes.INVOKEKIND.INVOKE_PROPERTYGET:
> >> _typeInfo.GetDocumentation(funcDesc.memid,
> >> out strName, out strDocString, out dwHelpContext, out strHelpFile);
> >> list.Add(strName);
> >> break;
> >> }
> >> }
> >> }
> >> catch (System.Exception ex)
> >> {
> >> throw ex;
> >> }
> >>
> >> return (string[])list.ToArray(typeof(string));
> >> }
> >>
> >> public string TypeName { get { return this._typeName; } }
> >> public string TypeFullName { get { return
> >> this.ComTypeLibrary.Name + "." + this._typeName; } }
> >> public string TypeDescription { get { return
> >> this._typeDescription; } }
> >> public int TypeHelpContext { get { return
> >> this._typeHelpContext; } }
> >> public string TypeHelpFile { get { return this._typeHelpFile; } }
> >> public object WrappedComObject { get { return _dispatch; } }
> >> public ComTypeLibrary ComTypeLibrary { get { return
> >> _comTypeLibrary; } }
> >> public System.Runtime.InteropServices.ComTypes.TYPEATTR
> >> TypeAttr { get { return
> >> (System.Runtime.InteropServices.ComTypes.TYPEATTR)Marshal.PtrToStructure(_pTypeAttr,
> >> typeof(System.Runtime.InteropServices.ComTypes.TYPEATTR)); } }
> >> public string TypeVersion
> >> {
> >> get
> >> {
> >> string version = String.Empty;
> >> try
> >> {
> >> version = TypeAttr.wMajorVerNum.ToString() + "." +
> >> TypeAttr.wMinorVerNum.ToString();
> >> }
> >> catch
> >> {
> >> }
> >> return version;
> >> }
> >> }
> >> }
> >> }
> >>
> >> /* ComTypeLibrary.cs */
> >> using System;
> >> using System.Collections.Generic;
> >> using System.IO;
> >> using System.Reflection;
> >> using System.Reflection.Emit;
> >> using System.Runtime.InteropServices;
> >> using System.Runtime.InteropServices.ComTypes;
> >> using System.Text;
> >>
> >> namespace SolidEdgeSpy.InteropServices
> >> {
> >> public class ComTypeLibrary
> >> {
> >> private System.Runtime.InteropServices.ComTypes.ITypeLib _typeLib;
> >> IntPtr _pTypeLibAttr = IntPtr.Zero;
> >> string _Name, _Description, _HelpFile;
> >> int _HelpContext;
> >>
> >> public
> >> ComTypeLibrary(System.Runtime.InteropServices.ComTypes.ITypeLib typeLib)
> >> {
> >> _typeLib = typeLib;
> >> _typeLib.GetLibAttr(out _pTypeLibAttr);
> >> _typeLib.GetDocumentation(-1, out _Name, out _Description,
> >> out _HelpContext, out _HelpFile);
> >> }
> >>
> >> public string Name { get { return this._Name; } }
> >> public string Description { get { return this._Description; } }
> >> public int HelpContext { get { return this._HelpContext; } }
> >> public string HelpFile { get { return this._HelpFile; } }
> >> public System.Runtime.InteropServices.ComTypes.TYPELIBATTR
> >> TypeLibAttr { get { return
> >> (System.Runtime.InteropServices.ComTypes.TYPELIBATTR)Marshal.PtrToStructure(_pTypeLibAttr,
> >> typeof(System.Runtime.InteropServices.ComTypes.TYPELIBATTR)); } }
> >> public string TypeLibVersion
> >> {
> >> get
> >> {
> >> string version = String.Empty;
> >> try
> >> {
> >> version = TypeLibAttr.wMajorVerNum.ToString() + "."
> >> + TypeLibAttr.wMinorVerNum.ToString();
> >> }
> >> catch
> >> {
> >> }
> >> return version;
> >> }
> >> }
> >>
> >> public Version Version
> >> {
> >> get
> >> {
> >> return new Version(TypeLibAttr.wMajorVerNum,
> >> TypeLibAttr.wMinorVerNum, 0, 0);