2013-06-03 1 views
5

Вот действительно простой .net < -> COM-пример взаимодействия с использованием событий.События, возникшие в .net-коде, не отображаются в COM-коде при развертывании с помощью боковых манифестах

Этот пример работает просто отлично, если я либо использую regasm, либо регистр для com interop вариант в Visual Studio для построения библиотеки .net. Но мне нужно разворачивать с помощью бесплатных бесплатных демонстраций регистрации без участия сторон.

Приложение работает очень хорошо в бок о бок режиме, это просто, что события, кажется, исчезает. Я подозреваю, что это проблема с сортировкой потоков, но я не могу найти правильное решение.

Это, конечно, попытка повторить проблему, имеющую несколько более сложную интеграцию интеграции. Существует одна разница между проблемами, которые возникают у меня здесь, по сравнению с реальными проблемами:

Оба решения не могут должным образом перегрузить события, создаваемые в .net-коде при работе в режиме бесплатного развертывания, и оба решения работают так, как ожидается, когда DLL .net зарегистрированы в реестре. Однако: в «реальном» проекте я получаю ошибку времени выполнения, когда она не работает с System.Reflection.Target. На этом упрощенном примере он просто терпит неудачу.

Я полностью придерживаюсь этого, поэтому любые предложения и решения будут очень приветствоваться.

Я поставил полный код на GitHub, если кому-то нужен, чтобы играть с ним, прежде чем ответить: https://github.com/Vidarls/InteropEventTest

В .NET часть

using System.Runtime.InteropServices; 
using System.Threading.Tasks; 

namespace InteropEventTest 
{ 
    [Guid("E1BC643E-0CCF-4A91-8499-71BC48CAC01D")] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [ComVisible(true)] 
    public interface ITheEvents 
    { 
     void OnHappened(string theMessage); 
    } 

    [Guid("77F1EEBA-A952-4995-9384-7228F6182C32")] 
    [ComVisible(true)] 
    public interface IInteropConnection 
    { 
     void DoEvent(string theMessage); 
    } 

    [Guid("2EE25BBD-1849-4CA8-8369-D65BF47886A5")] 
    [ClassInterface(ClassInterfaceType.None)] 
    [ComSourceInterfaces(typeof(ITheEvents))] 
    [ComVisible(true)] 
    public class InteropConnection : IInteropConnection 
    { 
     [ComVisible(false)] 
     public delegate void Happened(string theMessage); 
     public event Happened OnHappened; 


     public void DoEvent(string theMessage) 
     { 

      if (OnHappened != null) 
      { 
       Task.Factory.StartNew(() => OnHappened(theMessage)); 
      } 
     } 
    } 
} 

СОМ (VB6) часть

Private WithEvents tester As InteropEventTest.InteropConnection 

Private Sub Command1_Click() 
    Call tester.DoEvent(Text1.Text) 
End Sub 

Private Sub Form_Load() 
    Set tester = New InteropConnection 
End Sub 

Private Sub tester_OnHappened(ByVal theMessage As String) 
    Text2.Text = theMessage 
End Sub 

В настоящее время у меня есть следующие файлы/структура папок для г развертывания:

Root 
|-> [D] Interop.Event.Tester 
    |-> Interop.Event.Tester.manifest 
|-> [D] InteropEventTest 
    |-> InteropEventTest.dll 
|-> InteropEventTest.manifest 
|-> InteropEventTest.tlb 
|-> tester.exe 
|-> tester.exe.manifest 

Содержание файлов манифеста:

Interop.Event.Test.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 

<assemblyIdentity name="Interop.Event.Tester" version="1.0.0.0" type="win32" processorArchitecture="x86"/> 

</assembly> 

InteropEventTest.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 

<assemblyIdentity name="InteropEventTest" version="1.0.0.0" type="win32"/> 

<clrClass 
    name="InteropEventTest.InteropConnection" 
    clsid="{2EE25BBD-1849-4CA8-8369-D65BF47886A5}" 
    progid="InteropEventTest.InteropConnection" 
    runtimeVersion="v4.0.30319" 
    threadingModel="Both"/> 

<file name="InteropEventTest.tlb"> 
<typelib 
    tlbid="{5CD6C635-503F-4103-93B0-3EBEFB91E500}" 
    version="1.0" 
    helpdir="" 
    flags="hasdiskimage"/> 
</file> 
<comInterfaceExternalProxyStub 
    name="ITheEvents" 
    iid="{E1BC643E-0CCF-4A91-8499-71BC48CAC01D}" 
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}" 
    baseInterface="{00000000-0000-0000-C000-000000000046}" 
    tlbid="{5CD6C635-503F-4103-93B0-3EBEFB91E500}" /> 
</assembly> 

тэ ster.exe.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 

<assemblyIdentity name="tester.exe" version="1.0.0.0" type="win32" processorArchitecture="x86"/> 

<dependency> 
<dependentAssembly> 
    <assemblyIdentity name="InteropEventTest" version="1.0.0.0" type="win32"/> 
</dependentAssembly> 
</dependency> 

<dependency> 
<dependentAssembly> 
    <assemblyIdentity name="Interop.Event.Tester" version="1.0.0.0" type="win32" processorArchitecture="x86"/> 
</dependentAssembly> 
</dependency> 

</assembly> 

ответ

4

После долгого времени (и нескольких неудачных попыток) Оказалось, что я мог бы сделать эту работу, сделав одно малюсенькое изменение:

сделать VB6 код компилировать P- Код вместо собственного кода.

Я уверен, что это как-то влияет на то, как сортировка между потоками является ручкой, но я не смог найти никакой информации, подтверждающей эту теорию.

По крайней мере, это работает ...

Or Not! (24. октября 2013 года)

Оказалось, что в реальной жизни компиляция в P-Code недостаточна. В другой реализации этого шаблона мы закончили с событием , просто исчезающим в никуда, без каких-либо исключений (мы думали) и никаких следов. Так что еще расследование было вызвано:

1. Реальная проблема

Обертывание событие, запускающее в пункте примерки улова выяснилось, что там было на самом деле исключение броска, он просто никогда не всплыли где-нибудь

if (OnHappened != null)   
{ 
    try 
    { 
    OnHappened(theMessage)); 
    } 
    catch (Exception e) 
    { 
    Messagebox.Show(e.GetType().Name + " : " + e.message) 
    } 
} 

Исключение составляет TargetException (the object does not match the target type). Некоторые исследования показали, что это, скорее всего, в вопросе потоковая (как я подозревал раньше.)

2. Решение

Большая часть материала написанного об этом, казалось, решить с помощью метода Invoke. Оказалось, что большинство других людей, пытающихся решить эту проблему, создавали приложение winforms и, таким образом, имели удобный метод Ìnvoke(Delegate), доступный для всех форм и элементов управления.

Поскольку Winforms также выполняет довольно много COM-взаимодействия за кулисами (согласно теперь забытым статьям в списке результатов google) Метод invoke используется для гарантирует, что вызов метода выполняется в потоке, который создал заданный компонент и, таким образом, убедиться, что это происходит на потоке пользовательского интерфейса с передачей сообщений.

Я полагал, что это может иметь значение для моего дела, поэтому я обманул.

Я сделал свой класс Interop наследуются от WinForms управления

public class InteropConnection : Control, IInteropConnection 

Теперь я обернул мой вызов в методе Invoke

if (OnHappened != null)   
{ 
    try 
    { 
    Invoke(OnHappened, theMessage); 
    } 
    catch (Exception e) 
    { 
    Messagebox.Show(e.GetType().Name + " : " + e.message) 
    } 
} 

Теперь я получил исключение, потому что контроль не имел WindowHandle назначены.

Как оказалось, класс Control имеет удобный метод CreateHandle(), который может быть вызван и решает эту проблему. (Я не знаю, что возможные последствия это имеет, as the documentation does not recommend calling this method directly.

Теперь все, кажется, работает все время, хотя я не удивлюсь, если что-то новое подпрыгивает и кусает меня теперь ...

+1

Компиляция, как P-код [официальный Microsoft обходной] (http://stackoverflow.com/questions/2785169/using-the-net-backgroundworker-from -vb6-fail-with-an-accessviolationexception) к некоторым ошибкам COM-взаимодействия. Конечно, возможно, что вы испытываете совершенно несвязанный вопрос. – MarkJ

1

I столкнулись с той же проблемой. COM может маршировать событие/вызов в правильном потоке, но он должен иметь прокси-заглушку. Они добавляются в реестр, если вы используете опцию /tlb с regasm и эквивалент в файле манифеста представляют собой элементы typelib и comInterfaceExternalProxyStub. Исполняемый файл VB6 может быть скомпилирован в нативный двоичный файл.

Для получения дополнительной информации см Моя так тему: Regfree COM event fails from other thread

+0

Спасибо, я играл с прокси-сервером в манифесте, но я не мог заставить его работать. Пойду и посмотрим, смогу ли я работать с вашим методом. – Vidar