简体   繁体   English

简单的C ++ .Net控制台应用程序在64位Win7中崩溃

[英]Simple C++ .Net console app crashing in 64 bit Win7

My company uses a 3rd-party DLL to interface to some hardware (I've renamed it hwLib here). 我公司使用第3方DLL来连接某些硬件(在这里我将其重命名为hwLib )。 It was written long ago in VB6, I think. 我想这是很久以前用VB6编写的。 The DLL comes with an installer to register itself, etc. DLL带有安装程序以进行自身注册,等等。

We have a C# app that uses it and which runs just fine on XP and Win7, 32 or 64 bit. 我们有一个使用它的C#应用​​,它可以在XP和Win7(32或64位)上正常运行。 But I wrote a simple C++ console app which runs fine on XP/32 bit but crashes on Win7/64 bit.The console app looks like this, 但是我写了一个简单的C ++控制台应用程序,它可以在XP / 32位上正常运行,但在Win7 / 64位上崩溃。

#include "stdafx.h"
using namespace System;

int main(array<System::String ^> ^args)
{
    using namespace hwLib;
    ChwLib^ myLib = gcnew ChwLib();
    String^ str = myLib->GetDllVersion();
    Console::WriteLine(L"Hello hwLib");
    Console::WriteLine(str);
    Console::ReadLine();  //to keep window open til you hit the "any" key
    return 0;
}

Unhandled Exception: System.InvalidCastException: Unable to cast COM object of type 'hwLib.ChwLibClass' to interface t ype 'hwLib._ChwLib'. 未处理的异常:System.InvalidCastException:无法将类型为“ hwLib.ChwLibClass”的COM对象强制转换为接口“ hwLib._ChwLib”。

This operation failed because the QueryInterface call on the COM component for the interface with IID '{E0560D1E-9A54-4EBF-83E8-D7BD2C936512}' failed due to the following error: 该操作失败是因为对IID为“ {E0560D1E-9A54-4EBF-83E8-D7BD2C936512}”的接口的COM组件上的QueryInterface调用由于以下错误而失败:

No such interface supported (Exception from HRE SULT: 0x80004002 (E_NOINTERFACE)). 不支持这样的接口(HRE SULT的异常:0x80004002(E_NOINTERFACE))。 at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, Boolean& pfNeedsRelease) at hwLib.ChwLibClass.GetDllVersion() at main(String[] args) at mainCRTStartupStrArray(String[] arguments) 在System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc,IntPtr pCPCMD,Boolean&pfNeedsRelease)在hwLib.ChwLibClass.GetDllVersion()在main(String [] args)在mainCRTStartupStrArray(String []参数)

The C# program, which is a much bigger and more complex program, else I would post it here, runs without problems on the same system. C#程序是一个更大,更复杂的程序,否则我会在这里发布它,它在同一系统上运行没有问题。

COM is before my time - I took a course in it maybe 10 or 15 years ago but I don't remember it - Any suggestion on how to start debugging this? COM早于我的著作-我可能在10或15年前就参加了课程,但我不记得了-关于如何开始调试它的任何建议? Thanks!! 谢谢!!

COM provides thread-safety guarantees for a COM component that declares itself to not support threading. COM为声明自己不支持线程的COM组件提供线程安全保证。 Any component written in VB6 certainly does this. 用VB6编写的任何组件都可以做到这一点。 Directed by an entry in the registry named ThreadingModel. 由注册表中名为ThreadingModel的条目指示。

Your test program does not provide a safe home for such a component, your console mode app creates a Multi-Threaded Apartment, MTA for short. 您的测试程序无法为此类组件提供安全的家,您的控制台模式应用会创建一个多线程单元,简称MTA。 Which promises to not provide thread safety. 这保证不会提供线程安全性。 COM then creates its own STA thread to run the code for the component. 然后,COM创建自己的STA线程来运行该组件的代码。 Every call to the component will be marshaled from the main thread to that helper thread. 对组件的每次调用都将从主线程编组到该辅助线程。

But it then runs into a wall in your case, your component doesn't have the required proxy/stub registered. 但是,如果遇到这种情况,它就会碰壁,您的组件没有注册所需的代理/存根。 Extra code that COM needs to figure out how to copy the method's arguments. COM需要弄清楚如何复制该方法的参数的额外代码。 Easy in .NET thanks to Reflection, not in COM. 在.NET中很容易,这要归功于Reflection,而不是COM。 The proxy/stub is selected by entries in the HKCR\\Interface registry key, a VB6 component always uses the standard marshaller that works with the type library. 代理/存根由HKCR \\ Interface注册表项中的条目选择,VB6组件始终使用与类型库一起使用的标准编组器。 The E_NOINTERFACE error code is for the IMarshal interface, COM's last gasp to find a way, not implemented by VB6. E_NOINTERFACE错误代码用于IMarshal接口,这是COM的最后一次尝试,而不是由VB6实现。

Short from getting the registration right, the band-aid is to let your console mode app create an STA thread instead of an MTA thread. 除了获得正确的注册之外,临时帮助使控制台模式应用程序可以创建STA线程而不是MTA线程。 That's very easy to do, it just takes an attribute: 这很容易做到,只需要一个属性:

[STAThread]
int main(array<System::String ^> ^args)
// etc..

COM now no longer creates that helper thread and calls don't have to marshaled. 现在,COM不再创建该帮助程序线程,也不必整理呼叫。 This isn't actually enough, an STA thread must also pump a message loop. 实际上这还不够,STA线程还必须泵送消息循环。 Application::Run() as is probably used in your larger C# program. Application :: Run()可能在较大的C#程序中使用。 The message loop provides the way to marshal calls, very similar to Control.BeginInvoke() and Dispatcher.BeginInvoke(). 消息循环提供了封送呼叫的方式,与Control.BeginInvoke()和Dispatcher.BeginInvoke()非常相似。 You might get away with it since you don't actually make calls on the component from another thread. 因为您实际上并没有从另一个线程对该组件进行调用,所以您可能会放弃它。 But many COM components rely on the message loop to do their own stuff. 但是许多COM组件依靠消息循环来完成自己的工作。 You'll know you have a problem when you see deadlock or the component doesn't raise an event. 当您看到死锁或组件未引发事件时,您就会知道有问题。 The VB6 code could use a Timer for example, it won't tick without that message loop. 例如,VB6代码可以使用计时器,没有该消息循环就不会打勾。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM