我们有一个旧的32位Visual Studio C#Windows窗体解决方案,我们希望从现在开始以64位编译该解决方案。不幸的是,我们的应用程序使用了一些外部dll-s(用于扫描仪,照相机等),它们仅在32位版本中可用。 Accessing 32-bit DLLs from 64-bit code并非一帆风顺,尤其是当我们还要处理这些dll-s引发的事件时。我们在这方面的知识不足以根据本文创建实现,因此我们正在寻找更详细的说明或示例。
我们的首次尝试是基于this article。我们将第三方dll-s包装到一个后期绑定的32位COM服务器中,并按如下所述从64位应用程序中使用它(必要时作了必要修改,因为我们必须交换32位和64位的角色) 。尝试成功,但未完成,因为此解决方案无法将事件从COM服务器传递到64位客户端。因此,我们开始关注事件。我们找到了许多有关COM对象引发的消费事件的文章和示例,但是它们都没有为我们提供完整的解决方案。源的一部分专门与客户端或服务器唯一打交道,但它们彼此之间或与我们的环境(WinForm,c#)不兼容。
例如
也许其中一些可以由我们尽力使用,但是哪些以及如何使用?
修改
现在,我倾向于创建一个混合解决方案:对于从32位COM对象到调用者64位应用程序的反向通信,我的最新想法是将一个命名管道服务器放入64位应用程序和一个命名管道中客户端进入COM对象,并且每次在COM对象中引发事件时,它都会向Named Pipe服务器发送Named Pipe消息。我为此找到的代码here(项目CSNamedPipeServer和CSNamedPipeClient)可用。
答案 0 :(得分:3)
这里是一个带有64位服务器的示例,该示例在Windows系统代理dllhost托管的类库项目中作为C#类实现。
这是类代码(您可以将其编译为“任何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共同类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
答案 1 :(得分: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,而是将IMyClass
和OnMyEventDelegate
的定义复制到客户端的源代码中 >
(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
备注
将IMyClass
和OnMyEventDelegate
的定义添加到客户端的源代码中,而不是注册32位的NetComClassLibrary3.dll,也适用于32位客户端+ 64位COM服务器版本,但是引用了64位客户端中的32位COM dll导致BadImageFormat异常。