【发布时间】:2013-03-22 14:55:13
【问题描述】:
几年前,我继承了一个 C# 应用程序,该应用程序使用在非托管 C++ DLL 中定义的 COM 对象。从那以后,我一直在成功地调整对象接口,但是在(可能不相关的)VS2012 升级之后,函数签名的添加和更改突然受到随机异常的惩罚,例如 ExecutionEngineException 和 SEHException。
我的理解是该应用程序使用免注册 COM。没有 DLLRegisterServer 实现,我没有看到注册表中提到接口 guid,只有 C# 中的一个 ...
[ComImport,
Guid("C2427CB1-D6AE-49e8-B266-114F981C3353"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
SuppressUnmanagedCodeSecurity()]
public interface IDC
{
一个在 C++ 标头中。
interface __declspec(uuid("C2427CB1-D6AE-49e8-B266-114F981C3353"))
IDC : IUnknown
{
但可以肯定的是,我决定在这两个地方换一个新的 guid,并发现它完全阻止了 C# 识别该类:
System.InvalidCastException
Unable to cast COM object of type 'System.__ComObject' to interface type 'Apx.IDC'.
This operation failed because the QueryInterface call on the COM component for the
interface with IID '{the new guid}' failed due to the following error:
No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
(上述错误显然通常是由混合公寓状态引起的,但交换 guid 不会导致这种情况)。所以我推断一定有其他相关细节某处与我的更改相冲突。但那可能在哪里呢?感谢阅读...
编辑:
一个示例例外是......
First-chance exception at 0x000007feec748be4 (clr.dll) in Apex.exe: 0xC0000005:
Access violation reading location 0xffffffffffffffff.
The Common Language Runtime cannot stop at this exception. Common causes include:
incorrect COM interop marshalling, and memory corruption. To investigate further,
using native-only debugging.
An unhandled exception of type 'System.ExecutionEngineException' occurred in Apex.exe
为
[PreserveSig] [return: MarshalAs(UnmanagedType.I1)]
bool LoadDisplayList(IntPtr fileHandle, IntPtr pDisplayList,
UInt16 version, IntPtr pComparison);
和
virtual bool __stdcall LoadDisplayList(HANDLE fileHandle, class CDisplayList * pDisplayList,
WORD version, CDisplayList * pComparison) = 0;
堆栈跟踪结束
[Native to Managed Transition]
Apex.Graphics64.dll!CDisplayList::LoadRenderRecs(void * f=0x000000000000056c, CDisplayList * pComparison=0x0000000023ad2cf0) Line 1772 C++
Apex.Graphics64.dll!CDisplayList::Load(void * f=0x000000000000056c, unsigned short version=0x0002, CDisplayList * pComparison=0x0000000023ad2cf0) Line 1845 + 0x26 bytes C++
Apex.Graphics64.dll!CBaseDC::LoadDisplayList(void * f=0x000000000000056c, CDisplayList * pList=0x0000000023ad2cf0, unsigned short version=0x0002, CDisplayList * pComparison=0x0000000023ad2cf0) Line 1896 + 0x33 bytes C++
[Managed to Native Transition]
Apex.exe!Apex.DDC.LoadDisplayList(System.IO.FileStream file = {System.IO.FileStream}, Apex.DisplayList displayList = {Apex.DisplayList}, ushort version = 0x0002, Apex.DisplayList comparison = null) Line 1124 + 0xaf bytes C#
Apex.exe!Apex.DisplayList.Load(System.IO.FileStream f = {System.IO.FileStream}, ushort loadVersion = 0x0002, Apex.INode stubsRoot = {Apex.ViewPort3D}, Apex.DisplayList comparison = null) Line 166 + 0x53 bytes C#
确切的崩溃点有所不同 - 这里它实际上设法进入了 LoadDisplayList(),这比平时要好。由于崩溃表明堆损坏,我尝试将函数的签名剥离为无参数的 void 返回,将其内容减少为跟踪,并在对象创建后立即调用它 - 仍然会崩溃。如果我将相同的函数移到定义的顶部,它不会崩溃,在这种情况下,其他一些接口函数会崩溃,这让我认为它更有可能是一个 COM 问题,而不是算法级内存损坏。
【问题讨论】:
-
澄清:您一直在更改函数的签名,并且在双方更新和重建时都会发生异常。异常和调用堆栈的确切消息是什么?
-
@ZdeslavVojkovic,抱歉,该评论自行提交!我已将这些详细信息添加到说明中。
-
对我来说似乎是不同组件中的界面布局不匹配。我假设您在重新排序方法时修改了两个 dll。互操作dll呢?如果你不更新它,那么编组将失败。
-
@ZdeslavVojkovic:是的,我什至构建了一个简单的解析器来仔细检查排序。除了 C# EXE 和 C++ DLL 之外,唯一的其他 DLL 是 EXE 内控件的 ActiveX 包装器。
-
只是好奇,为什么要重复 C# 中的接口定义?这是不好的做法。您可以使用 TlbImp.exe 实用程序导入类型库,它会自动创建具有正确接口定义的程序集。
标签: c# c++ com visual-studio-2012