VBAクライアントにイベントを公開して起動しようとしています。これまでのところ、VBAクライアント側では、イベントが公開されており、メソッドイベント処理メソッドがモジュールクラスに追加されていますが、VBAイベント処理メソッドは起動しません。何らかの理由で、デバッグ時にイベントがnullになります。同期でコードを変更しても、効果はありませんでした。
記録のために、私は他のSO=質問をチェックしましたが、助けにはなりませんでした。
良い答えがあれば高く評価されます。
[ComVisible(true)]
[Guid("56C41646-10CB-4188-979D-23F70E0FFDF5")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IWebEvents))]
[ProgId("MyAssembly.MyClass")]
public class MyClass : ServicedComponent, IMyClass
{
public string _address { get; private set; }
public string _filename { get; private set; }
[DispId(4)]
public void DownloadFileAsync(string address, string filename)
{
_address = address;
_filename = filename;
System.Net.WebClient wc = new System.Net.WebClient();
Task.Factory.StartNew(() => wc.DownloadFile(_address, _filename))
.ContinueWith((t) =>
{
if (null != this.OnDownloadCompleted)
OnDownloadCompleted();
});
}
public event OnDownloadCompletedEventHandler OnDownloadCompleted;
}
[ComVisible(false)]
public delegate void OnDownloadCompletedEventHandler();
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IWebEvents
{
[DispId(1)]
void OnDownloadCompleted();
}
これはあなたのすべてのバウンティハンター、200 repポイントにとって良いギグです
.NETコードの重要な概念は、イベントを別のインターフェイスのメソッドとして定義し、それを_[ComSourceInterfacesAttribute]
_を介してクラスに接続することです。例では、これはこのコード[ComSourceInterfaces(typeof(IEvents))]
で行われます。ここで、IEvents
インターフェイスは、COMクライアントで処理する必要があるイベントを定義します。
イベントの命名に関する注意:
c#クラスで定義されたイベント名とインターフェイスで定義されたインターフェイスメソッド名は同じである必要があります。この例では、_IEvents::OnDownloadCompleted
_は_DemoEvents::OnDownloadCompleted
_に対応しています。
次に、クラス自体のパブリックAPIを表す2番目のインターフェースが定義されます。ここでは、IDemoEvents
と呼ばれます。このインターフェイスでは、COMクライアントで呼び出されるメソッドが定義されています。
C#コード(COMVisibleEvents.dllにビルド)
_using System;
using System.EnterpriseServices;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace COMVisibleEvents
{
[ComVisible(true)]
[Guid("8403C952-E751-4DE1-BD91-F35DEE19206E")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IEvents
{
[DispId(1)]
void OnDownloadCompleted();
}
[ComVisible(true)]
[Guid("2BF7DA6B-DDB3-42A5-BD65-92EE93ABB473")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IDemoEvents
{
[DispId(1)]
Task DownloadFileAsync(string address, string filename);
}
[ComVisible(true)]
[Guid("56C41646-10CB-4188-979D-23F70E0FFDF5")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IEvents))]
[ProgId("COMVisibleEvents.DemoEvents")]
public class DemoEvents
: ServicedComponent, IDemoEvents
{
public delegate void OnDownloadCompletedDelegate();
private event OnDownloadCompletedDelegate OnDownloadCompleted;
public string _address { get; private set; }
public string _filename { get; private set; }
private readonly string _downloadToDirectory =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
public async Task DownloadFileAsync(string address, string filename)
{
try
{
using (WebClient webClient = new WebClient())
{
webClient.Credentials = new NetworkCredential(
"user", "psw", "domain");
string file = Path.Combine(_downloadToDirectory, filename);
await webClient.DownloadFileTaskAsync(new Uri(address), file)
.ContinueWith(t =>
{
// https://stackoverflow.com/q/872323/
var ev = OnDownloadCompleted;
if (ev != null)
{
ev();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
catch (Exception ex)
{
// Log exception here ...
}
}
}
}
_
ファイルのダウンロードに関する注意:
ファイルをダウンロードするには、メソッド_WebClient.DownloadFileTaskAsync
_を使用します。指定したリソースを、タスクオブジェクトを使用した非同期操作としてローカルファイルにダウンロードします。
タスクオブジェクトは通常、メインアプリケーションスレッドで同期的にではなく、スレッドプールスレッドで非同期的に実行されます。したがって、メインスレッドでContinueWith
を呼び出す必要があります。そうしないと、OnDownloadCompleted
イベントを実行できません。そのため、ContinueWith(continuationAction, TaskScheduler.FromCurrentSynchronizationContext)
が使用されます。
レガスム
_C:\Windows\Microsoft.NET\Framework\v4.0.30319>regasm C:\Temp\COMVisibleEvents\bin\Debug\COMVisibleEvents.dll /tlb: C:\Temp\COMVisibleEvents\bin\Debug\COMVisibleEvents.tlb
_
_
*.tlb
_ファイルへのVBAクライアント参照
regasm
によって生成された_*tlb
_への参照を追加します。ここで、このtlb
ファイルの名前はCOMVisibleEvents
です。
ここでは、ExcelユーザーフォームがVBAクライアントとして使用されました。ボタンがクリックされた後、メソッドDownloadFileAsync
が実行され、このメソッドが完了すると、イベントがハンドラー_m_eventSource_OnDownloadCompleted
_でキャッチされました。この例では、C#プロジェクトCOMVisibleEvents.dllのソースコードをドロップボックスからダウンロードできます。
VBAクライアントコード(MS Excel 2007)
_Option Explicit
Private WithEvents m_eventSource As DemoEvents
Private Sub DownloadFileAsyncButton_Click()
m_eventSource.DownloadFileAsync "https://www.dropbox.com/s/0q3dskxopelymac/COMVisibleEvents.zip?dl=0", "COMVisibleEvents.Zip"
End Sub
Private Sub m_eventSource_OnDownloadCompleted()
MsgBox "Download completed..."
End Sub
Private Sub UserForm_Initialize()
Set m_eventSource = New COMVisibleEvents.DemoEvents
End Sub
_
結果