[英]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, 例如,
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,复制的定义IMyClass
和OnMyEventDelegate
到客户端的源代码
(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. 备注添加的定义IMyClass
和OnMyEventDelegate
到客户端的源代码,而不是注册的32位NetComClassLibrary3.dll工作还对32位客户端+ 64位COM服务器的版本,但在引用32位COM DLL的64位客户端导致BadImageFormat异常。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.