我可以从我的外部.dll 访问我 c# 对象吗?也许使用腐

标签: dll C#
发布时间: 2013/12/6 21:24:19
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

我有一个 c# 应用程序在本地,运行也用 c# 编写的一个.dll。

MyApplication 包含

namespace MyApplication
{
     public interface IMyInterface
     {
          IMyInterface Instance { get; }
          int MyProp { get; }
     }

     class MyClass : IMyInterface
     {
          public int MyProp { get; private set; }

          private MyClass instance
          public static IMyInterface
          {
              get
              {
                  if (instance == null)
                  {
                      instance = new MyClass();
                  }
                  return instance;
              }
          }

          private MyClass() { MyProp = 1; }
     }
}

MyLibrary:

namespace MyLibrary
{
    public LibClass
    {
         public static int GetProp()
         {
              //I want to get the running instance of MyApplication
              //If MyApplication is not running I want to start a new instance
              //From my application instance I want to 
              //return the value from MyInterface.Instance.MyProp
         }
    }
}

一些谷歌上搜索我看 COM 服务器的排序,但不清楚如果这就是最好的办法。我甚至知道什么对谷歌的这一麻烦了最终 MyInterface 将会复杂得多,并将包括事件,以通知 MyLibrary 刷新的数据。如何做到这一点的最佳方法?它是一个 COM 服务器吗?我想要创造出某种 API 中的 MyApplicationMyLibrary 使用吗?

附加信息:

我.dll 正在创建 excel 为实时数据服务器。我希望能够从 excel 中访问我的应用程序数据,并将通知 excel 刷新。我不想要我的应用程序的多个实例,因为用户输入将确定 excel 中显示的值。我能够创建 rtd 服务器,但我不相信什么最好的方法是访问我的外部数据。

编辑:

后做一些更多的研究,我觉得我很感兴趣使用 GetActiveObject("MyApplication.IMyInterface")MyLibrary ,看起来像

namespace MyLibrary
{
    public LibClass
    {
         public static int GetProp()
         {
            running_obj = System.Runtime.InteropServices.Marshal.GetActiveObject("MyApplication.IMyInterface")
            return ((IMyInterface) running_obj).MyProp;
         }
         private object running_obj = null;
    }
}

但不知道如何注册 MyApplication.MyClass 在 ROT 中。如是的代码引发异常

Invalid class string (Exception from HRESULT: 0x800401F3 (CO_E_CLASSSTRING))

解决方法 1:

我的解决方案

  • 如果有人知道更好的方式来做这很想知道。
  • 我也仍然会收到将事件添加到我的 COMinterface 的问题。我想添加 public event ComEvent MyApplicationClose;MyApplication ,当它关闭时调用。

编辑

能够获得事件以使用托管事件接收器工作了都反映在下面的代码编辑。

帮助器类

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 );

        /// <summary>
        /// Get a snapshot of the running object table (ROT).
        /// </summary>
        /// <returns>A hashtable mapping the name of the object
        //     in the ROT to the corresponding object</returns>

        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;
        }
    }
}

我的应用程序注册到 ROT 中

我的应用程序将作为数据服务器。它接收并处理来自多个源的数据。对此数据的访问被公开通过 com。只有一个实例的 MyApplication 减少到连接到外部数据源和加工,同时允许多个客户端要使用它获取的数据的冗余。

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
        }
    }
}

注:这包含生成后事件注册程序集

C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe $(TargetFileName) /codebase /tlb:$(TargetName)TypeLib.tlb

使用通过 COM MyApplication

因为它是通过 COM 它应该与任何 COM 语言一起工作,不过我有只试过 c#。最初这段代码将放入 RTDserver 为 excel。这将允许用户生成复杂的工作表,利用从我的应用程序的实时数据。

首先创建一个包装点净中为我的 COM 对象。

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;

    }
}

现在我可以在我的.net 应用程序中使用包装器

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.");
        }
    }
}

其中的输出是

Val = 1
Val = 2

你关闭的 COM 服务器应用程序后您会看到关闭消息。

Val = 1
Val = 2
Com Application Closed.

引用

在.NET 中,消费者在 MFC 中运行对象表: 提供程序

自动化的特定实例的 Visual Studio.NET 中使用 C#

注册到 ROT 中的对象

赞助商