从64位c#WinForms应用程序调用第三方32位dll时的事件处理

问题描述 投票:2回答:2

我们有一个旧的32位Visual Studio C#Windows窗体解决方案,我们希望从现在开始以64位编译。不幸的是,我们的应用程序使用了一些仅在32位版本中可用的外部dll-s(用于扫描仪,相机等)。 Accessing 32-bit DLLs from 64-bit code并不简单,尤其是当我们想要处理那些dll-s引发的事件时。我们在这方面的知识不足以根据本文创建实现,因此我们正在寻找更详细的说明或示例。

我们的第一次尝试基于this article。我们将第三方dll-s包装到一个后期绑定的32位COM服务器中,我们在这里描述的64位应用程序中使用它(加以必要的修改,因为我们必须交换32位和64位的角色) 。此尝试是成功的,但不完整,因为此解决方案不会将事件从COM服务器传递到64位客户端。所以我们开始关注这些事件。我们发现了很多关于消耗COM对象引发的事件的文章和示例,但它们都没有为我们提供完整的解决方案。消息来源的一部分专门与客户端或专门与服务器交易,但它们彼此不兼容,或与我们的环境(WinForm,c#)不兼容。

例如,

  1. this answer告诉我们如何创建一个COM服务器,它将.NET事件暴露给VBA客户端,但我不知道如何从c#客户端使用它。
  2. 相比之下,this article为现有的COM服务器提供了一个很好的工作c#客户端,但是没有说明如何制作这样的COM服务器(这个COM服务器明显不同于前面的例子)
  3. this answer没有说明解决方案的任何细节。
  4. This article用于c ++而不是c#。
  5. This answer指的是this article,但后者再次使用VB客户端而不是c#。
  6. This article以无法追踪的方式混合不同的东西。

也许我们可以通过一些努力来使用其中的一些,但是哪些以及如何?

编辑

现在我倾向于创建一个混合解决方案:从32位COM对象到调用者64位应用程序的向后通信的最新想法是将命名管道服务器放入64位应用程序,将命名管道客户端放入COM对象以及每次在COM对象中引发事件时,它都会向命名管道服务器发送命名管道消息。我找到的代码是here(项目CSNamedPipeServer和CSNamedPipeClient)。

c# winforms event-handling com-interop
2个回答
3
投票

下面是一个带有64位服务器的示例,它在类库项目中作为C#类实现,由Windows的系统代理:dllhost托管。

这是类代码(你可以编译为'任何cpu',不需要编译为x64):

namespace NetComClassLibrary3
{
    // technically, we don't *have to* define an interface, we could do everything using dynamic stuff
    // but it's more practical so we can reference this .NET dll from our client
    [ComVisible(true)]
    [Guid("31dd1263-0002-4071-aa4a-d226a55116bd")]
    public interface IMyClass
    {
        event OnMyEventDelegate OnMyEvent;
        object MyMethod();
    }

    // same remark than above.
    // This *must* match the OnMyEvent signature below
    [ComVisible(true)]
    [Guid("31dd1263-0003-4071-aa4a-d226a55116bd")]
    public delegate void OnMyEventDelegate(string text);

    // this "event" interface is mandatory
    // note from the .NET perspective, no one seems to implement it
    // but it's referenced with the ComSourceInterfaces attribute on our COM server (below)
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("31dd1263-0000-4071-aa4a-d226a55116bd")]
    public interface IMyEvents
    {
        // dispids are mandatory here otherwise you'll get a DISP_E_UNKNOWNNAME error
        [DispId(1)]
        void OnMyEvent(string text);
    }

    [ComVisible(true)]
    [ComSourceInterfaces(typeof(IMyEvents))]
    [Guid("31dd1263-0001-4071-aa4a-d226a55116bd")]
    public class MyClass : IMyClass
    {
        public event OnMyEventDelegate OnMyEvent;

        public object MyMethod()
        {
            // we use the current running process to test out stuff
            // this should be Windows' default surrogate: dllhost.exe
            var process = Process.GetCurrentProcess();
            var text = "MyMethod. Bitness: " + IntPtr.Size + " Pid: " + process.Id + " Name: " + process.ProcessName;
            Console.WriteLine(text); // should not be displayed when running under dllhost
            OnMyEvent?.Invoke("MyEvent. " + text);
            return text;
        }
    }
}

这是我注册它的方式(注意我的目标是64位注册表):

%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm.exe NetComClassLibrary3.dll /codebase /tlb

这是一个.reg,以确保它将在dllhost.exe中运行进程外(guid是COM coclass MyClass'guid):

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\AppID\{31dd1263-0001-4071-aa4a-d226a55116bd}]
"DllSurrogate"=""

[HKEY_CLASSES_ROOT\CLSID\{31dd1263-0001-4071-aa4a-d226a55116bd}]
"AppID"="{31dd1263-0001-4071-aa4a-d226a55116bd}"

这是客户端,编译为x86:

using System;
using NetComClassLibrary3; // we can reference the .net dll as is

namespace ConsoleApp10
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Bitness: " + IntPtr.Size);
            // note we don't use new MyClass() otherwise we may go inprocess
            var type = Type.GetTypeFromCLSID(typeof(MyClass).GUID);
            var obj = (IMyClass)Activator.CreateInstance(type);

            // note I'm using the beloved dynamic keyword here. for some reason obj.OnMyEvent works but locally raises a cast error I've not investigated further...
            dynamic d = obj;
            d.OnMyEvent += (OnMyEventDelegate)((t) =>
            {
                Console.WriteLine(t);
            });
            Console.WriteLine(obj.MyMethod());
        }
    }
}

当我运行它时,这是输出:

Bitness: 4 // running as 32-bit
MyEvent. MyMethod. Bitness: 8 Pid: 23780 Name: dllhost // from 64-bit world
MyMethod. Bitness: 8 Pid: 23780 Name: dllhost // from 64-bit world

0
投票

当我们在Simon Mourier的解决方案中交换32位和64位的角色时,那么 - 除了改变编译位数 - 我们应该改变4件事。

(1)更改注册

%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm.exe NetComClassLibrary3.dll /codebase /tlb

%windir%\Microsoft.NET\Framework\v4.0.30319\regasm.exe NetComClassLibrary3.dll /codebase /tlb

(2)更改注册表项

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\AppID\{31dd1263-0001-4071-aa4a-d226a55116bd}]
"DllSurrogate"=""

[HKEY_CLASSES_ROOT\CLSID\{31dd1263-0001-4071-aa4a-d226a55116bd}]
"AppID"="{31dd1263-0001-4071-aa4a-d226a55116bd}"

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\AppID\{31dd1263-0001-4071-aa4a-d226a55116bd}]
"DllSurrogate"=""

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{31dd1263-0001-4071-aa4a-d226a55116bd}]
"AppID"="{31dd1263-0001-4071-aa4a-d226a55116bd}"

(3)在64位客户端中,不是注册32位NetComClassLibrary3.dll,而是将IMyClassOnMyEventDelegate的定义复制到客户端的源代码中

(4)同样在客户端,

更改

var type = Type.GetTypeFromCLSID(typeof(MyClass).GUID);

var type = Type.GetTypeFromProgID("NetComClassLibrary3.MyClass");

所以客户看起来如此:

using System;
// removed by mma - using NetComClassLibrary3; // we can reference the .net dll as is

namespace ConsoleApp10
{
    // inserted by mma:
    [System.Runtime.InteropServices.Guid("31dd1263-0002-4071-aa4a-d226a55116bd")]
    public interface IMyClass
    {
        event OnMyEventDelegate OnMyEvent;
        object MyMethod();
    }
    [System.Runtime.InteropServices.Guid("31dd1263-0002-4071-aa4a-d226a55116bd")]
    public delegate void OnMyEventDelegate(string text);
    // end of insertion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Bitness: " + IntPtr.Size);
            // note we don't use new MyClass() otherwise we may go inprocess
            // removed by mma var type = Type.GetTypeFromCLSID(typeof(MyClass).GUID);
            // inserted by mma:
            var type = Type.GetTypeFromProgID("NetComClassLibrary3.MyClass");
            // end of insertion
            var obj = (IMyClass)Activator.CreateInstance(type);

            // note I'm using the beloved dynamic keyword here. for some reason obj.OnMyEvent works but locally raises a cast error I've not investigated further...
            dynamic d = obj;
            d.OnMyEvent += (OnMyEventDelegate)((t) =>
            {
                Console.WriteLine(t);
            });
            Console.WriteLine(obj.MyMethod());
        }
    }
}

所以输出会改变

Bitness: 4 // running as 32-bit
MyEvent. MyMethod. Bitness: 8 Pid: 23780 Name: dllhost // from 64-bit world
MyMethod. Bitness: 8 Pid: 23780 Name: dllhost // from 64-bit world

Bitness: 8 // running as 64-bit
MyEvent. MyMethod. Bitness: 4 Pid: 56140 Name: dllhost // from 32-bit world
MyMethod. Bitness: 4 Pid: 56140 Name: dllhost // from 32-bit world

备注将IMyClassOnMyEventDelegate的定义添加到客户端的源代码中而不是注册32位NetComClassLibrary3.dll也适用于32位客户端+ 64位COM服务器版本,但引用了32位COM dll 64位客户端导致BadImageFormat异常。

© www.soinside.com 2019 - 2024. All rights reserved.