为什么当从面向任何 CPU 的 C# 项目调用此代码时,此代码会引发 System.AccessViolationExc
Why does this code throw System.AccessViolationException when called from a C# project targeting Any CPU?
我的ATL项目中有这个IDL:
[
object,
uuid(61B0BFF7-E9DF-4D7E-AFE6-49CC67245257),
dual,
nonextensible,
pointer_default(unique)
]
interface ICrappyCOMService : IDispatch {
typedef
[
uuid(C65F8DE6-EDEF-479C-BD3B-17EC3F9E4A3E),
version(1.0)
]
struct CrapStructure {
INT ErrorCode;
BSTR ErrorMessage;
} CrapStructure;
[id(1)] HRESULT TestCrap([in] INT errorCode, [in] BSTR errorMessage, [in, out] CrapStructure *crapStructure);
};
[
uuid(763B8CA0-16DD-48C8-BB31-3ECD9B9DE441),
version(1.0),
]
library CrappyCOMLib
{
importlib("stdole2.tlb");
[
uuid(F7375DA4-2C1E-400D-88F3-FF816BB21177)
]
coclass CrappyCOMService
{
[default] interface ICrappyCOMService;
};
};
这是我在C++中的实现:
STDMETHODIMP CCrappyCOMService::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* const arr[] = {
&IID_ICrappyCOMService
};
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
if (InlineIsEqualGUID(*arr[i], riid))
return S_OK;
}
return S_FALSE;
}
STDMETHODIMP CCrappyCOMService::TestCrap(INT errorCode, BSTR errorMessage, CrapStructure *crapStructure) {
memset(crapStructure, 0, sizeof(CrapStructure));
crapStructure->ErrorCode = errorCode;
crapStructure->ErrorMessage = errorMessage;
CComPtr<ICreateErrorInfo> x;
ICreateErrorInfo* pCreateErrorInfo;
CreateErrorInfo(&pCreateErrorInfo);
pCreateErrorInfo->AddRef();
pCreateErrorInfo->SetDescription(errorMessage);
pCreateErrorInfo->SetGUID(IID_ICrappyCOMService);
pCreateErrorInfo->SetSource(L"Component.TestCrap");
IErrorInfo* pErrorInfo;
pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void**)&pErrorInfo);
pErrorInfo->AddRef();
SetErrorInfo(0, pErrorInfo);
pErrorInfo->Release();
pCreateErrorInfo->Release();
printf("Going to return %d...n", errorCode);
return errorCode;
}
我在 C# 中这样称呼它:
static void Main(string[] args)
{
var service = new CrappyCOMService();
var crapStructure = new CrapStructure();
try
{
service.TestCrap(-1, "This is bananas.", ref crapStructure);
}
catch (COMException exception)
{
Console.WriteLine(exception.ErrorCode);
Console.WriteLine(exception.Message);
}
Console.WriteLine(crapStructure.ErrorCode);
Console.WriteLine(crapStructure.ErrorMessage);
}
如果我从面向 x64 的 C# 项目运行代码,那么一切正常。问题是,当我从面向 C# 中的任何 CPU 的项目调用TestCrap
方法时,它会抛出System.AccessViolationException
。为什么?我可以验证当它抛出System.AccessViolationException
时,它仍然打印将返回 -1...到控制台窗口。
编辑:我已经缩小了复制步骤。对不起,但我忘了添加这个。当使用 x64 版本(面向 x64 的 ATL 项目和面向任何 CPU 的 C# 测试项目(编译我的代码,然后转换回任何 CPU 构建(面向 Win32 的 ATL 项目和面向任何 CPU 的 C# 测试项目(编译我的代码时,似乎会发生这种情况。
编辑:我已经进一步缩小了错误范围。首先,看起来 Any CPU C# 项目始终使用我的 COM 对象的 x64 版本,因此需要首先编译我的 ATL 项目的版本才能传播任何代码,因为 x64 系统上的任何 CPU 实际上都将在 x64 上下文中运行。此外,它看起来像线,crapStructure->ErrorMessage = errorMessage;
导致System.AccessViolationException
。它将在 COM 世界中执行代码,但在返回到 C# 世界中时,它会引发异常。
编辑:在C++中,printf("%dn", sizeof(CrapStructure));
结果为16。从 C# 开始,Console.WriteLine(Marshal.SizeOf(typeof(CrapStructure)));
也会产生16。但是,唉,再读一遍,这是一个无用的检查,正如汉斯在这里的回答所提到的:如何检查结构消耗的字节数?
编辑:我尝试做tlbimp CrappyCOM.dll /out:CrappyCOMx86Managed.dll
并使用我的任何面向CPU的C#项目中的CrappyCOMx86Managed.dll
,但它再次抛出了System.AccessViolationException
。我认为这是因为它再次转到系统上注册的x64 COM对象,因此我使用regsvr32
注销x64 COM对象。再次运行代码并注册 x86 COM 对象,它抱怨检索具有 CLSID {F7375DA4-2C1E-400D-88F3-FF816BB21177} 的组件的 COM 类工厂由于以下错误而失败:80040154类未注册(HRESULT 的异常:0x80040154 (REGDB_E_CLASSNOTREG((。我发现让它从我的tlbimp
生成的存根中面向 x86 COM 对象的唯一方法是将我的 C# 项目的构建目标修改为 x86,然后代码用于测试我的 x86 COM 对象,所以这对我来说似乎是一个有争议的问题,因为我希望让任何 CPU 专门针对我的 x86 COM 对象或我的 x64 COM 对象正确的结构尺寸。COM 在 C# 中是一场灾难。
可能发生的情况是(隐式地(使用相同的类型库(.TLB(,或包含 .TLB,适用于 x64 和 x86 版本。
问题是您的 TLB 定义了一个非托管结构,并且此结构布局在 32 位和 64 位模式下会有所不同(大小、偏移量等(。
在 .NET 中,您希望确保在使用不同的处理器体系结构进行编译时不会引用完全相同的互操作程序集(通过隐式 tlbimp COM 引用从 TLB 生成(。
使用标准的Visual Studio工具进行正确的操作可能很棘手。
如果您仍然想使用这样的结构(这在自动化世界中有点奇怪(,我建议您自己使用 tlbimp.exe 构建互操作程序集,并像普通 .NET 程序集一样简单地引用这些互操作程序集,但取决于位数(您将能够使用 .csproj 中的"条件"属性进行调整(,而不是直接使用 COM 引用。
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 代码在main()中运行,但在函数中出现错误
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 编译包含字符串的代码时遇到问题
- 我在c++代码中生成了一个运行时#3异常
- 如何在linux终端中同时编译和运行c++代码
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 在Linux for Windows上编译C++代码时出错
- 我的字符计数代码计算错误.为什么
- 孤立代码块在结构中引发异常
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 此代码是否违反一个定义规则
- 为什么我的代码在输出中增加了93天
- 我的简单if-else语句是如何无法访问的代码
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 为什么在这个代码结束循环中没有得到结束
- 在c代码之间共享数据的最佳方式
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值