COM无法在进程外启动.Net服务器编译为AnyCPU
本文关键字:服务器 Net 编译 AnyCPU 启动 进程 COM | 更新日期: 2023-09-27 18:24:58
我正在尝试让COM启动进程外的.NET COM服务器。如果服务器进程是用x64编译的,它会工作,但如果我使用AnyCPU(这正是我想要的),它会挂起一段时间,最终以0x80080005(CO_E_server_EXEC_FAILURE)失败。我怎样才能让它发挥作用?
- 我在一台64位计算机上运行:带有Visual Studio 2008 SP1的Windows 7
- 我可以在任务管理器中看到它确实启动了我的服务器。所以我想问题出在COM和服务器之间的通信上(类注册)
- 我的测试客户端应用程序是用C#编写的,但它是为x86还是x64编译并不重要。用32位C++编写的东西也会出现这个问题
- 如果我使用x64重建服务器并运行它,然后重建为AnyCPU,则COM可以启动它。重新启动将使我回到原始状态。也许COM事先不知道要使用什么比特,而之前的执行会有所帮助
- 我找到了Andy McMullen的博客文章,并尝试将CLSCTX_ACTIVATE_64_BIT_SERVER传递给CoCreateInstance(),但这提前触发了一个故障:0x80040154(REGDB_E_CLASSNOTREG)。我在COM注册中是否做错了什么?你可以在下面看到它非常简单。当以64位运行时会发生注册,当客户端为64位时会出现问题,因此不应涉及Wow6432Node
另一个小伙子也有类似的问题,但MSFT的答案令人困惑。他似乎在暗示它只能通过DCOM(见链接)或COM+工作。我怀疑这两者都将是一项艰巨的工作,而且比分发我的x64和x86版本的.exe要糟糕得多。
您可能想知道我为什么要实现IPersistFile。这是因为我真正的问题是让BindMoniker()从一个32位C++程序工作到我的AnyCPU.Net程序。我已经将我的问题简化为这里给出的更简单的例子。
这是客户端代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
static extern object CoCreateInstance(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
[MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter,
CLSCTX dwClsContext,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
[Flags]
enum CLSCTX : uint
{
CLSCTX_LOCAL_SERVER = 0x4,
CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000,
}
private void Form1_Load(object sender, EventArgs e)
{
IPersistFile pf = (IPersistFile)CoCreateInstance(
new Guid("1984D314-FC8D-44bc-9146-8A13500666A6"),
null,
CLSCTX.CLSCTX_LOCAL_SERVER,
new Guid("0000010b-0000-0000-C000-000000000046")); // IPersistFile
pf.Load("c:''bozo", 0);
}
}
这是服务器:
static class Program
{
[STAThread]
static void Main()
{
if (Environment.CommandLine.Contains("/reg")) {
RegistryKey cls = Registry.LocalMachine.CreateSubKey(String.Format(
"SOFTWARE''Classes''CLSID''{0}", PersistFile.ClassID.ToString("B")));
cls.SetValue("InprocHandler32", "Ole32.dll");
RegistryKey ls32 = cls.CreateSubKey("LocalServer32");
ls32.SetValue(null, '"' + Application.ExecutablePath + '"');
ls32.SetValue("ServerExecutable", Application.ExecutablePath);
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
RegistrationServices reg = new RegistrationServices();
reg.RegisterTypeForComClients(
typeof(PersistFile),
RegistrationClassContext.LocalServer,
RegistrationConnectionType.MultipleUse);
Application.Run(new Form1());
}
}
[ComVisible(true),
Guid("1984D314-FC8D-44bc-9146-8A13500666A6"),
ClassInterface(ClassInterfaceType.None)]
public class PersistFile : IPersistFile
{
public static Guid ClassID
{
get
{
GuidAttribute a = (GuidAttribute)typeof(PersistFile).GetCustomAttributes(typeof(GuidAttribute), false)[0];
return new Guid(a.Value);
}
}
#region IPersistFile
public void GetClassID(out Guid pClassID)
{
MessageBox.Show("GetClassID");
pClassID = ClassID;
}
public int IsDirty()
{
MessageBox.Show("IsDirty");
return 1;
}
public void Load(string pszFileName, int dwMode)
{
MessageBox.Show(String.Format("Load {0}", pszFileName));
}
public void Save(string pszFileName, bool fRemember)
{
MessageBox.Show("Save");
throw new NotImplementedException();
}
public void SaveCompleted(string pszFileName)
{
MessageBox.Show("SaveCompleted");
throw new NotImplementedException();
}
public void GetCurFile(out string ppszFileName)
{
MessageBox.Show("GetCurFile");
throw new NotImplementedException();
}
#endregion
}
我想问题出在运行时。我创建了一个COM服务器,它使用C++库进行注册(注册过程完美无瑕)。我在从.NET(CS)切换到AnyCPU时遇到了问题。
架构:
- C++库接口COM(在x64和x86平台上构建)
- .NET库包装(CS)(正确实例化所需的x64/x86 C++库)
- .NET应用程序(CS)-COM客户端或COM服务器
当注册构建为"AnyCPU"的.NET应用程序时,会发生一些丑陋的事情。一旦COM客户端通过DCOM调用COM服务器,服务器应用程序就会启动,但客户端收到无法启动COM服务器的错误。
我更进一步,用procmon和其他工具分析了注册数据,得出了同样的结论:
- x86在classes''Wow6432Node中注册类
- x64和AnyCPU在classes中注册类(在x64 windows计算机上,密钥完全相同;我打赌x86和AnyCPU会在x86计算机上注册相同的密钥)
现在,我做了更多的实验:x86/x64/AnyCPU COM客户端可以毫无问题地连接到任何x86/x64 COM服务器,但无论如何都无法连接到AnyCPU COM服务器。。。
然后我执行了以下测试用例:
- 请注册x86 COM服务器,将可执行文件替换为AnyCPU COM服务器:COM客户端启动x86 COM服务器时,没有通信。。。它一次又一次地启动服务器
- 注册x64 COM服务器,将可执行文件替换为AnyCPU COM服务器:COM客户端正在启动x64 COM服务器但没有通信。。。它一次又一次地启动服务器
- 注册AnyCPU COM服务器,将可执行文件替换为x86 COM服务器:COM客户端能够成功启动并连接到x86 COM服务器
- 注册AnyCPU COM服务器,将可执行文件替换为x64 COM服务器:COM客户端能够成功启动并连接到x64 COM服务器
- 注册x86 COM服务器,将可执行文件替换为x64 COM服务器:COM客户端能够成功启动并连接到x64 COM服务器
- 注册x64 COM服务器,将可执行文件替换为x86 COM服务器:COM客户端能够成功启动并连接到x86 COM服务器
沟通问题到底在哪里?这很奇怪。。。所提供的解决方案(CLSCTX_ACTIVATE-64_BIT_SERVER、PreferredServerBitness或corflags)都没有帮助。
还有谁在这件事上取得了一些进展?我们应该联系微软吗?
尝试使用RegistrationServices类来注册com程序集。它还将选择正确的注册表路径并执行其他一些操作。
示例:
Assembly currentAssembly = Assembly.GetExecutingAssembly();
System.Runtime.InteropServices.RegistrationServices regAsm = new System.Runtime.InteropServices.RegistrationServices();
bool isRegistered = regAsm.RegisterAssembly(currentAssembly, System.Runtime.InteropServices.AssemblyRegistrationFlags.SetCodeBase);
另外,我认为,根据.NET com服务器,.NET客户端程序集有一些问题,但我找不到任何资源…
希望,这会有所帮助。。。