Ключевым понятием в .net код для определения события (ы) в качестве метода (ов) на отдельном интерфейсе и подключить его к класс через [ComSourceInterfacesAttribute]
. В примере это делается с помощью этого кода [ComSourceInterfaces(typeof(IEvents))]
, где интерфейс IEvents
определяет события (события), которые должны обрабатываться на COM-клиенте.
Примечание к именам событий: имена событий, определенные в классах класса C#, и имена методов интерфейса, определенные на интерфейсе, должны быть одинаковыми. В этом примере IEvents::OnDownloadCompleted
соответствует DemoEvents::OnDownloadCompleted
.
Затем определяется второй интерфейс, представляющий публичный API самого класса, здесь он называется 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 =>
{
if (OnDownloadCompleted != null)
{
OnDownloadCompleted();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
catch (Exception ex)
{
// Log exception here ...
}
}
}
}
Примечание к файлу загрузки: Для загрузки файла метод WebClient.DownloadFileTaskAsync
используется. Он загружает указанный ресурс в локальный файл в виде асинхронной операции с использованием объекта задачи. Объект задачи обычно выполняется асинхронно в потоке пула потоков, а не синхронно в основном потоке приложения. Поэтому необходимо называть ContinueWith
в основном потоке, потому что в противном случае невозможно выполнить событие OnDownloadCompleted
. Вот почему используется ContinueWith(continuationAction, TaskScheduler.FromCurrentSynchronizationContext)
.
Regasm
C:\Windows\Microsoft.NET\Framework\v4.0.30319>regasm C:\Temp\COMVisibleEvents\bin\Debug\COMVisibleEvents.dll /tlb: C:\Temp\COMVisibleEvents\bin\Debug\COMVisibleEvents.tlb
ссылка клиент VBA в *.tlb
файл
Добавить ссылку на *tlb
, который был сгенерирован с помощью regasm
. Здесь имя этого файла tlb
: COMVisibleEvents
.
Здесь Excel Форма пользователя была использована в качестве клиента VBA. После нажатия кнопки был выполнен метод DownloadFileAsync
, и когда этот метод завершен, событие было уловлено в обработчике m_eventSource_OnDownloadCompleted
. В этом примере вы можете загрузить исходные коды проекта C# COMVisibleEvents.dll из моего dropbox.
код 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
Результат
Попробуйте объявить MyClass родителя = это до задачи. В вашей задаче используйте parent.OnDownloadCompleted. – Jules
@jules Какова логика этого? –
«this» в вашем коде относится к Task not MyClass. – Jules