2009-03-16 15 views
6

tlbimp.exeを使用してInteropアセンブリを生成するためにVSにインポートした従来のVB6コンポーネントがあります。 VB6コンポーネントは、VB6内でメッセージを渡すことができるイベントを定義します。interopを使用してvb6イベントを呼び出す

Public Event Message(ByVal iMsg As Variant, oCancel As Variant) 

私は実際にも、私のC#のプログラムでこれを調達できるようにしたいのですが、そのイベントは、デリゲートではないか、有益な何か他のものとしてインポートばかり。だから、私は聞くことができますが、決して発砲することはできません。誰もVB6に含まれるイベントを起動する方法を知っていますか? C#イベントは次のようになります

[TypeLibType(16)] 
[ComVisible(false)] 
public interface __MyObj_Event 
{ 
    event __MyObj_MessageEventHandler Message; 
} 

残念ながら、VB6コードは変更できません。ありがとう。

+0

オブジェクトがイベントを発生させている場合は、そのイベントだけを聴くことができます。どのような意味でC#から発射したいのですか?誰がそのイベントを処理しますか? – Groo

+0

すでにvb6アプリに登録されていますが、実際にはイベントよりもデリゲートの方が多いので、私はそれを上げることができると期待していました – Steve

答えて

3

VB6では、イベントは、イベントを宣言するクラス(または場合によってはフォーム)内からのみ呼び出すことができます。 VB6でイベントを強制的に発生させるには、クラスのメソッドを公開してこれを行う必要があります。あなたがソースコードを持っていなければ、あなたは不運です。ドキュメント

RaiseEvent eventnameにから

[(ArgumentListは)]

必要eventnameには、モジュール 内で宣言されたイベント の名前で、 命名規則基本変数に従います。悪いニュースの無記名であることが申し訳

例えば

Option Explicit 

Private FText As String 

Public Event OnChange(ByVal Text As String) 

'This exposes the raising the event 

Private Sub Change(ByVal Text As String) 
    RaiseEvent OnChange(Text) 
End Sub 

Public Property Get Text() As String 
    Text = FText 
End Property 


Public Property Let Text(ByVal Value As String) 
    FText = Value 
    Call Change(Value) 
End Property 

+1

ありがとう、私は今私のコーヒーに泣くでしょう:( – Steve

6

実際、希望はまだ失われていません。 は、COMオブジェクト上のイベントをオブジェクトのクラスの外部から呼び出すことが可能なです。この機能は、COM自体によって実際に提供されますが、間接的には機能します。

COMでは、イベントはパブリッシュ/サブスクライブモデルで機能します。イベントを持つCOMオブジェクト(「イベントソース」)はイベントをパブリッシュし、1つ以上の他のCOMオブジェクトはイベントハンドラをソースオブジェクトに添付することでイベントにサブスクライブします(ハンドラは「イベントシンク」と呼ばれます)。通常、ソースオブジェクトは、すべてのイベントシンクをループし、適切なハンドラメソッドを呼び出すだけで、イベントを発生させます。

これはどのように役立ちますか? COMは、ソースオブジェクトのイベントに現在サブスクライブしているすべてのイベントシンクオブジェクトのリストをイベントソースに照会することができます。イベントシンクオブジェクトのリストを取得したら、各シンクオブジェクトのイベントハンドラを呼び出すことによってイベントを発生させることをシミュレートできます。

注:私はオーバー簡素化の詳細をし、用語の一部とリベラルいるんだけど、それはイベントがCOMでどのように動作するかの短い(とやや政治的に正しくない)バージョンです。

この知識を利用して、COMオブジェクトのイベントを外部コードから呼び出すことができます。実際には、C#でSystem.Runtime.InteropSystem.Runtime.Interop.ComTypes名前空間のCOM相互運用機能のサポートを利用して、このすべてを行うことができます。


EDIT

私はあなたが.NETからCOMオブジェクトのイベントを発生させることを可能にするユーティリティクラスを書きました。使用するのはかなり簡単です。私は被害妄想だとに対処するための良い方法を望んでいたので、以下

MyObj legacyComObject = new MyObj(); 

// The following code assumes other COM objects have already subscribed to the 
// MyObj class's Message event at this point. 
// 
// NOTE: VB6 objects have two hidden interfaces for classes that raise events: 
// 
// _MyObj (with one underscore): The default interface. 
// __MyObj (with two underscores): The event interface. 
// 
// We want the second interface, because it gives us a delegate 
// that we can use to raise the event. 
// The ComEventUtils.GetEventSinks<T> method is a convenience method 
// that returns all the objects listening to events from the legacy COM object. 

// set up the params for the event 
string messageData = "Hello, world!"; 
bool cancel = false; 

// raise the event by invoking the event delegate for each connected object... 
foreach(__MyObj sink in ComEventUtils.GetEventSinks<__MyObj>(legacyComObject)) 
{ 
    // raise the event via the event delegate 
    sink.Message(messageData, ref cancel); 

    if(cancel == true) 
    { 
     // do cancel processing (just an example) 
     break; 
    } 
} 

ComEventUtilsクラス(だけでなく、ヘルパークラス、SafeIntPtrのコードは、されています。ここでは、あなたの質問からイベントインターフェイスを使用した例がありますCOM関連のコードが必要とするIntPtr S):

免責事項私は徹底的に以下のコードをテストしていません。コードはいくつかの場所で手動でのメモリ管理を行います。したがって、コードにメモリリークが発生する可能性があります。また、これは単なる例であるため、コードにエラー処理を追加しませんでした。注意して使用してください。

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Runtime.InteropServices; 
using COM = System.Runtime.InteropServices.ComTypes; 

namespace YourNamespaceHere 
{ 

/// <summary> 
/// A utility class for dealing with COM events. 
/// Needs error-handling and could potentially be refactored 
/// into a regular class. Also, I haven't extensively tested this code; 
/// there may be a memory leak somewhere due to the rather 
/// low-level stuff going on in the class, but I think I covered everything. 
/// </summary> 
public static class ComEventUtils 
{ 
    /// <summary> 
    /// Get a list of all objects implementing an event sink interface T 
    /// that are listening for events on a specified COM object. 
    /// </summary> 
    /// <typeparam name="T">The event sink interface.</typeparam> 
    /// <param name="comObject">The COM object whose event sinks you want to retrieve.</param> 
    /// <returns>A List of objects that implement the given event sink interface and which 
    /// are actively listening for events from the specified COM object.</returns> 
    public static List<T> GetEventSinks<T>(object comObject) 
    { 
     List<T> sinks = new List<T>(); 
     List<COM.IConnectionPoint> connectionPoints = GetConnectionPoints(comObject); 

     // Loop through the source object's connection points, 
     // find the objects that are listening for events at each connection point, 
     // and add the objects we are interested in to the list. 
     foreach(COM.IConnectionPoint connectionPoint in connectionPoints) 
     { 
      List<COM.CONNECTDATA> connections = GetConnectionData(connectionPoint); 

      foreach (COM.CONNECTDATA connection in connections) 
      { 
       object candidate = connection.pUnk; 

       // I tried to avoid relying on try/catch for this 
       // part, but candidate.GetType().GetInterfaces() kept 
       // returning an empty array. 
       try 
       { 
        sinks.Add((T)candidate); 
       } 
       catch { } 
      } 

      // Need to release the interface pointer in each CONNECTDATA instance 
      // because GetConnectionData implicitly AddRef's it. 
      foreach (COM.CONNECTDATA connection in connections) 
      { 
       Marshal.ReleaseComObject(connection.pUnk); 
      } 
     } 

     return sinks; 
    } 

    /// <summary> 
    /// Get all the event connection points for a given COM object. 
    /// </summary> 
    /// <param name="comObject">A COM object that raises events.</param> 
    /// <returns>A List of IConnectionPoint instances for the COM object.</returns> 
    private static List<COM.IConnectionPoint> GetConnectionPoints(object comObject) 
    { 
     COM.IConnectionPointContainer connectionPointContainer = (COM.IConnectionPointContainer)comObject; 
     COM.IEnumConnectionPoints enumConnectionPoints; 
     COM.IConnectionPoint[] oneConnectionPoint = new COM.IConnectionPoint[1]; 
     List<COM.IConnectionPoint> connectionPoints = new List<COM.IConnectionPoint>(); 

     connectionPointContainer.EnumConnectionPoints(out enumConnectionPoints); 
     enumConnectionPoints.Reset(); 

     int fetchCount = 0; 
     SafeIntPtr pFetchCount = new SafeIntPtr(); 

     do 
     { 
      if (0 != enumConnectionPoints.Next(1, oneConnectionPoint, pFetchCount.ToIntPtr())) 
      { 
       break; 
      } 

      fetchCount = pFetchCount.Value; 

      if (fetchCount > 0) 
       connectionPoints.Add(oneConnectionPoint[0]); 

     } while (fetchCount > 0); 

     pFetchCount.Dispose(); 

     return connectionPoints; 
    } 

    /// <summary> 
    /// Returns a list of CONNECTDATA instances representing the current 
    /// event sink connections to the given IConnectionPoint. 
    /// </summary> 
    /// <param name="connectionPoint">The IConnectionPoint to return connection data for.</param> 
    /// <returns>A List of CONNECTDATA instances representing all the current event sink connections to the 
    /// given connection point.</returns> 
    private static List<COM.CONNECTDATA> GetConnectionData(COM.IConnectionPoint connectionPoint) 
    { 
     COM.IEnumConnections enumConnections; 
     COM.CONNECTDATA[] oneConnectData = new COM.CONNECTDATA[1]; 
     List<COM.CONNECTDATA> connectDataObjects = new List<COM.CONNECTDATA>(); 

     connectionPoint.EnumConnections(out enumConnections); 
     enumConnections.Reset(); 

     int fetchCount = 0; 
     SafeIntPtr pFetchCount = new SafeIntPtr(); 

     do 
     { 
      if (0 != enumConnections.Next(1, oneConnectData, pFetchCount.ToIntPtr())) 
      { 
       break; 
      } 

      fetchCount = pFetchCount.Value; 

      if (fetchCount > 0) 
       connectDataObjects.Add(oneConnectData[0]); 

     } while (fetchCount > 0); 

     pFetchCount.Dispose(); 

     return connectDataObjects; 
    } 
} //end class ComEventUtils 

/// <summary> 
/// A simple wrapper class around an IntPtr that 
/// manages its own memory. 
/// </summary> 
public class SafeIntPtr : IDisposable 
{ 
    private bool _disposed = false; 
    private IntPtr _pInt = IntPtr.Zero; 

    /// <summary> 
    /// Allocates storage for an int and assigns it to this pointer. 
    /// The pointed-to value defaults to 0. 
    /// </summary> 
    public SafeIntPtr() 
     : this(0) 
    { 
     // 
    } 

    /// <summary> 
    /// Allocates storage for an int, assigns it to this pointer, 
    /// and initializes the pointed-to memory to known value. 
    /// <param name="value">The value this that this <tt>SafeIntPtr</tt> points to initially.</param> 
    /// </summary> 
    public SafeIntPtr(int value) 
    { 
     _pInt = Marshal.AllocHGlobal(sizeof(int)); 
     this.Value = value; 
    } 

    /// <summary> 
    /// Gets or sets the value this pointer is pointing to. 
    /// </summary> 
    public int Value 
    { 
     get 
     { 
      if (_disposed) 
       throw new InvalidOperationException("This pointer has been disposed."); 
      return Marshal.ReadInt32(_pInt); 
     } 

     set 
     { 
      if (_disposed) 
       throw new InvalidOperationException("This pointer has been disposed."); 
      Marshal.WriteInt32(_pInt, Value); 
     } 
    } 

    /// <summary> 
    /// Returns an IntPtr representation of this SafeIntPtr. 
    /// </summary> 
    /// <returns></returns> 
    public IntPtr ToIntPtr() 
    { 
     return _pInt; 
    } 

    /// <summary> 
    /// Deallocates the memory for this pointer. 
    /// </summary> 
    public void Dispose() 
    { 
     if (!_disposed) 
     { 
      Marshal.FreeHGlobal(_pInt); 
      _disposed = true; 
     } 
    } 

    ~SafeIntPtr() 
    { 
     if (!_disposed) 
      Dispose(); 
    } 

} //end class SafeIntPtr 

} //end namespace YourNamespaceHere 
関連する問題