Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h1><strong>My Solution</strong></h1> <ul> <li>If anyone knows of a better way to do this I would love to know.</li> <li>I am also still having problems adding an event to my COMinterface. I would like to add <code>public event ComEvent MyApplicationClose;</code> inside <code>MyApplication</code> to be called when it is closed.</li> </ul> <h2><em>Edit</em></h2> <p>I was able to get events to work using <a href="http://blogs.msdn.com/b/rcook/archive/2008/11/01/iconnectionpoint-and-net-or-how-i-learned-to-stop-worrying-and-love-managed-event-sinks-part-1-sample-3.aspx" rel="nofollow">Managed Event Sinks</a>. <em>Edits are reflected in code below.</em></p> <h3>Helper class</h3> <pre><code>namespace ole32 { public class Ole32 { [DllImport( "Ole32.Dll" )] public static extern int CreateBindCtx( int reserved, out IBindCtx bindCtx ); [DllImport( "oleaut32.dll" )] public static extern int RegisterActiveObject( [MarshalAs( UnmanagedType.IUnknown )] object punk, ref Guid rclsid, uint dwFlags, out int pdwRegister ); [DllImport( "ole32.dll", EntryPoint = "GetRunningObjectTable" )] public static extern int GetRunningObjectTable( int reserved, out IRunningObjectTable ROT ); [DllImport( "ole32.dll", EntryPoint = "CreateItemMoniker" )] public static extern int CreateItemMoniker( byte[] lpszDelim, byte[] lpszItem, out IMoniker ppmk ); /// &lt;summary&gt; /// Get a snapshot of the running object table (ROT). /// &lt;/summary&gt; /// &lt;returns&gt;A hashtable mapping the name of the object // in the ROT to the corresponding object&lt;/returns&gt; public static Hashtable GetRunningObjectTable() { Hashtable result = new Hashtable(); IntPtr numFetched = IntPtr.Zero; IRunningObjectTable runningObjectTable; IEnumMoniker monikerEnumerator; IMoniker[] monikers = new IMoniker[1]; GetRunningObjectTable( 0, out runningObjectTable ); runningObjectTable.EnumRunning( out monikerEnumerator ); monikerEnumerator.Reset(); while ( monikerEnumerator.Next( 1, monikers, numFetched ) == 0 ) { IBindCtx ctx; CreateBindCtx( 0, out ctx ); string runningObjectName; monikers[0].GetDisplayName( ctx, null, out runningObjectName ); object runningObjectVal; runningObjectTable.GetObject( monikers[0], out runningObjectVal ); result[runningObjectName] = runningObjectVal; } return result; } } } </code></pre> <h3>My application registered in the ROT</h3> <p>My application will act as a data server. It receives and processes data from multiple sources. Access to this data is exposed through COM. Having only one instance of MyApplication reduces redundancy to of connections to outside data sources and the processing, while allowing multiple clients to use the data it gets.</p> <pre><code>namespace MyNamespace { [ComVisible( true ), GuidAttribute( "14C09983-FA4B-44e2-9910-6461728F7883" ), InterfaceType( ComInterfaceType.InterfaceIsDual )] public interface ICOMApplication { [DispId(1)] int GetVal(); } //Events for my com interface. Must be IDispatch [Guid( "E00FA736-8C24-467a-BEA0-F0AC8E528207" ), InterfaceType( ComInterfaceType.InterfaceIsIDispatch ), ComVisible( true )] public interface ICOMEvents { [DispId( 1 )] void ComAppClose( string s ); } public delegate void ComEvent( string p ); [ComVisible(true)] [Guid( "ECE6FD4C-52FD-4D72-9668-1F3696D9A99E" )] [ComSourceInterfaces( typeof( ICOMWnEvents) )] [ClassInterface( ClassInterfaceType.None )] public class MyApplication : ICOMApplication, IDisposable { //ICOMEvent public event ComEvent ComAppClose; protected MyApplication () { //check if application is registered. //if not registered then register the application if (GetApiInstance() == null) { Register_COMI(); } } // UCOMI-Version to register in the ROT protected void Register_COMI() { int errorcode; IRunningObjectTable rot; IMoniker moniker; int register; errorcode = Ole32.GetRunningObjectTable( 0, out rot ); Marshal.ThrowExceptionForHR( errorcode ); errorcode = BuildMoniker( out moniker ); Marshal.ThrowExceptionForHR( errorcode ); register = rot.Register( 0, this, moniker ); } public void Dispose() { Close( 0 ); //close and clean up } //Will look for an existing instance in the ROT and return it public static ICOMApplication GetApiInstance() { Hashtable runningObjects = Ole32.GetRunningObjectTable(); IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator(); while ( rotEnumerator.MoveNext() ) { string candidateName = (string) rotEnumerator.Key; if ( !candidateName.Equals( "!MyNamespace.ICOMApplication" ) ) continue; ICOMApplication wbapi = (ICOMApplication ) rotEnumerator.Value; if ( wbapi != null ) return wbapi; //TODO: Start the application so it can be added to com and retrieved for use } return null; } //Builds the moniker used to register and look up the application in the ROT private static int BuildMoniker( out IMoniker moniker ) { UnicodeEncoding enc = new UnicodeEncoding(); string delimname = "!"; byte[] del = enc.GetBytes( delimname ); string itemname = "MyNamespace.ICOMApplication"; byte[] item = enc.GetBytes( itemname ); return Ole32.CreateItemMoniker( del, item, out moniker ); } protected void Close( int i ) { //Deregistering from ROT should be automatic //Additional cleanup if (ComAppClose != null) ComAppClose(""); } ~MyApplication() { Dispose(); } //implement ICOMApplication interface private static int i = 0; public int GetVal() { return i++; //test value to return } } } </code></pre> <p><em>Note:</em> This contains a Post-build event to register the assembly</p> <pre><code>C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe $(TargetFileName) /codebase /tlb:$(TargetName)TypeLib.tlb </code></pre> <h3>Using MyApplication through COM</h3> <p>Since it is through COM it should work with any COM language, however I have only tried c#. Initially this code will be put into RTDserver for excel. This will allow the user to build complex worksheets that utilize real time data from my application.</p> <p>First I create a wrapper in dot net for my COM object.</p> <pre><code>namespace COMTest { //extend both the com app and its events and use the event sink to get the events [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] public sealed class MyAppDotNetWrapper, ICOMEvents, ICOMApplication { private ICOMApplication comapp; public MyAppDotNetWrapper() { StartEventSink() } //Manage the sink events private void StartEventSink() { //get the instance of the app; comapp = MyApplication .GetApiInstance(); if (comapp != null) { serverconnected = true; //Start the event sink IConnectionPointContainer connectionPointContainer = (IConnectionPointContainer) comapp; Guid comappEventsInterfaceId = typeof (ICOMApplicationEvents).GUID; connectionPointContainer.FindConnectionPoint(ref comappEventsInterfaceId, out connectionPoint); connectionPoint.Advise(this, out cookie); } } private void StopEventSink() { if (serverconnected) { //unhook the event sink connectionPoint.Unadvise(cookie); connectionPoint = null; } } //Implement ICOMApplication methods public int GetVal() { return comapp.GetVal(); } //receive sink events and forward public event ComEvent ComAppCloseEvent; public void ComAppClose(string s) { serverconnected = false; ComAppCloseEvent(s); } private ICOMApplication comapp; IConnectionPoint connectionPoint; private int cookie; private bool serverconnected; } } </code></pre> <p>Now I can use my wrapper in my .net application</p> <pre><code>namespace COMTest { class Program { private static MyAppDotNetWrapper app; static void Main( string[] args ) { //create a new instance of the wrapper app = new MyAppDotNetWrapper(); //Add the onclose event handler app.ComAppCloseEvent += OnAppClose; //call my com interface method Console.WriteLine("Val = " + app.GetVal().ToString()); Console.WriteLine("Val = " + app.GetVal().ToString()); string s = Console.ReadLine(); } static voic OnClose(string s) { Console.WriteLine("Com Application Closed."); } } } </code></pre> <p>Where the output is</p> <pre><code>Val = 1 Val = 2 </code></pre> <p>And after you close the COM server application you see the close message.</p> <pre><code>Val = 1 Val = 2 Com Application Closed. </code></pre> <h2><em>References</em></h2> <p><a href="http://www.codeproject.com/Articles/17490/Running-Object-Table-Provider-in-NET-consumer-in-M" rel="nofollow">Running Object Table: Provider in .NET, consumer in MFC</a></p> <p><a href="http://www.codeproject.com/Articles/7984/Automating-a-specific-instance-of-Visual-Studio-NE" rel="nofollow">Automating a specific instance of Visual Studio .NET using C#</a></p> <p><a href="http://www.developmentnow.com/g/21_2004_5_0_0_105861/Register-an-Object-in-the-ROT.htm" rel="nofollow">Register an Object in the ROT</a></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload