简体   繁体   English

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

[英]Event handling at calling a third party 32-bit dll from a 64-bit c# WinForms application

We have an old 32-bit Visual Studio C# Windows Forms solution, which we want to compile from now on, in 64-bit. 我们有一个旧的32位Visual Studio C#Windows窗体解决方案,我们希望从现在开始以64位编译该解决方案。 Unfortunately, our application uses some external dll-s (for scanners, cameras and so on) which are available only in 32-bit version. 不幸的是,我们的应用程序使用了一些外部dll-s(用于扫描仪,照相机等),它们仅在32位版本中可用。 Accessing 32-bit DLLs from 64-bit code isn't straightforward, especially, when we want to handle also the events raised by those dll-s. 从64位代码访问32位DLL并不是一件容易的事,尤其是当我们还要处理那些dll-s引发的事件时。 Our knowledge at this area is unsufficient for creating an implementation based on this article, so we are looking for more detailed instructions or examples. 我们在这方面的知识不足以根据本文创建实现,因此我们正在寻找更详细的说明或示例。

Our first attempt was based on this article . 我们的首次尝试是基于本文 We wrapped the third party dll-s into a late bound 32-bit COM server and we used it from our 64-bit application as described here (mutatis mutandis, because we had to swap the roles of 32-bit and 64-bit). 我们将第三方dll-s包装到一个后期绑定的32位COM服务器中,并按如下所述从64位应用程序中使用它(必要时作了必要修改,因为我们必须交换32位和64位的角色) 。 This attempt was successful, but incomplete, because this solution doesn't deliver the events from the COM server to the 64-bit client. 尝试成功,但未完成,因为此解决方案无法将事件从COM服务器传递到64位客户端。 So we started focusing on the events. 因此,我们开始关注事件。 We found a lot of articles and examples dealing with consuming events raised by the COM object, but none of them provides a complete solution for us. 我们找到了许多有关COM对象引发的消费事件的文章和示例,但是它们都没有为我们提供完整的解决方案。 One part of the sources deals exclusively with the client, or exclusively with the server, but they are incompatible with each other, or with our environment (WinForm, c#). 一部分资源专门与客户端或服务器打交道,但它们彼此之间或与我们的环境(WinForm,c#)不兼容。

For example, 例如,

  1. this answer tells us how to make a COM server which exposes .NET events to a VBA client, but I don't know how to use it from ac# client. 此答案告诉我们如何制作将.NET事件公开给VBA客户端的COM服务器,但我不知道如何从ac#客户端使用它。
  2. In contrast this article gives a good working c# client for an existing COM server, but doesn't tell, how to make such a COM server (this COM server is visibly different from the previous example) 相比之下, 本文为现有的COM服务器提供了一个运行良好的c#客户端,但没有说明如何制作这样的COM服务器(该COM服务器与上例明显不同)
  3. this answer doesn't tell any details of the solution. 这个答案没有透露解决方案的任何细节。
  4. This article is for c++ instead of c#. 本文适用于c ++而不是c#。
  5. This answer refers to this article , but the latter uses again a VB client instead of c#. 此答案是针对本文的 ,但后者再次使用VB客户端而不是c#。
  6. This article mixes different things in an untraceable manner. 本文以一种无法追踪的方式混合了不同的事物。

Perhaps some of these can be used by us with some effort, but which and how? 也许其中一些可以由我们尽力而为,但是哪些以及如何使用?

Edit 编辑

Now I tend to create a hybrid solution: My newest idea for the backward communication from the 32-bit COM object to the caller 64-bit application is to put a Named Pipe server into the 64-bit application and a Named Pipe client into the COM object and every time when an event is raised in the COM object, it sends a Named Pipe message to the Named Pipe server. 现在,我倾向于创建一个混合解决方案:对于从32位COM对象到调用者64位应用程序的反向通信,我的最新想法是将Named Pipe服务器放入64位应用程序中,并将Named Pipe客户端放入Windows Server 2003中。 COM对象,并且每次在COM对象中引发事件时,它都会向Named Pipe服务器发送Named Pipe消息。 The code I found for this is available here (projects CSNamedPipeServer and CSNamedPipeClient). 我为此找到的代码在此处可用(项目CSNamedPipeServer和CSNamedPipeClient)。

Here is a sample with a 64-bit server, implemented as a C# class in a class library project, hosted by Windows' system surrogate: dllhost. 这是一个带有64位服务器的示例,该示例在Windows系统代理dllhost托管的类库项目中作为C#类实现。

This is the class code (you can compile as 'any cpu', no need to compile as x64): 这是类代码(您可以将其编译为“任何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;
        }
    }
}

This is how I register it (note I'm targeting the 64bit registry): 这是我注册的方式(请注意,我的目标是64位注册表):

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

This is a .reg to make sure it will run out-of-process in dllhost.exe (the guid is the COM coclass MyClass' guid): 这是一个.reg以确保它会在dllhost.exe中运行超出进程(GUID是COM共同类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}"

And here is the client, compiled as x86: 这是编译为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());
        }
    }
}

When I run it, this is the output: 当我运行它时,这是输出:

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

When we swap the role of 32-bit and 64-bit in the solution of Simon Mourier, then - beyond changing the compilation bitness - we should change 4 things. 当我们在Simon Mourier的解决方案中交换32位和64位的角色时,除了更改编译位以外,我们还应该更改4件事。

(1) changing the registration (1)变更注册

from

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

to

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

(2) changing registry items (2)更改注册表项

from

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}"

to

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) In the 64-bit client, instead of registering the 32-bit NetComClassLibrary3.dll, copy the definitions of IMyClass and OnMyEventDelegate into the client's source code (3)在64位的客户端,而不是注册的32位NetComClassLibrary3.dll,复制的定义IMyClassOnMyEventDelegate到客户端的源代码

(4) Also in the client, (4)在客户中

change 更改

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

to

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

so the client will look so: 因此客户端看起来像这样:

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());
        }
    }
}

and so the output will change from 所以输出将从

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

to

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

Remark Adding the definitions of IMyClass and OnMyEventDelegate into the client's source code instead of registering the 32-bit NetComClassLibrary3.dll works also on the 32-bit client + 64-bit COM-server version, but referencing the 32-bit COM dll in the 64-bit client leads to a BadImageFormat exception. 备注添加的定义IMyClassOnMyEventDelegate到客户端的源代码,而不是注册的32位NetComClassLibrary3.dll工作还对32位客户端+ 64位COM服务器的版本,但在引用32位COM DLL的64位客户端导致BadImageFormat异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Environment.Is64BitProcess从c#应用程序动态调用32位或64位DLL - Dynamically calling 32-bit or 64-bit DLL from c# application using Environment.Is64BitProcess 我可以从 64 位 C# Winforms 应用程序调用 MAPI32.DLL > MAPISendMail 吗? - Can I call MAPI32.DLL > MAPISendMail from a 64-bit C# Winforms application? 调用 64 位 C/C++ DLL 的 C# 应用程序崩溃,但 32 位版本运行 - C# app calling 64-bit C/C++ DLL crashes, but 32-bit version runs C#:根据平台访问32位/ 64位DLL - C#: Access 32-bit/64-bit DLL depending on platform 使用32位应用程序中的64位库 - Use 64-bit library from 32-bit application 在 64 位机器上以 32 位方式运行 C# 应用程序 - Running a C# application as 32-bit on a 64-bit machine 将在64位系统上构建的C#中的应用程序与在32位系统上运行的应用程序匹配 - Matching application in C# that built on 64-bit system to running on 32-bit system 我可以在32位系统上运行在64位系统上开发的C#应用​​程序吗? - Can I run a C# application developed on a 64-bit system on a 32-bit system? C# 32 位和 64 位应用程序的单一部署项目 - C# Single Deployment project for 32-bit and 64-bit application 当完整路径解析为64位目录时,无法从32位C#应用程序启动快捷方式(lnk)文件 - Unable to launch shortcut (lnk) files from 32-bit C# application when the full path resolves to a 64-bit directory
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM