为啥我的 C# 和 C++ dll 表现出不同的行为?

Posted

技术标签:

【中文标题】为啥我的 C# 和 C++ dll 表现出不同的行为?【英文标题】:Why do my C# and C++ dlls exhibit different behavior?为什么我的 C# 和 C++ dll 表现出不同的行为? 【发布时间】:2012-04-22 04:33:51 【问题描述】:

我正在开展一个项目,该项目涉及创建一个支持特定接口的 dll,以便插入某些软件以向其添加功能。这是由调用我的 dll 的 dll 完成的(我没有执行调用的 dll 的源代码)。最初,我得到了一个接口和一个创建 COM 可见 dll 的 C# 实现。然而,在使用了一段时间后,我发现我想使用一些大型 C++ 库,并且由于创建包装器需要很长时间,所以我考虑创建一个 C++ ATL COM dll。我这样做了,我的类的方法似乎被正确调用(我注册了我的 dll,运行程序并且方法似乎以正确的顺序被调用),但是我发现一些行为不同。

我不确定如何解释这一点,因为我的代码与封闭源 API 相关,但如果我描述一个示例,也许有人可能会对我想要查看的位置有一些想法。

例如,在 C# dll 中,我尝试通过以下方式打开文件:

FMANFileControl fileControl = new FMANFileControl();
FMANFile wFile = null;
const string filePath = @"C:\Data\April 4\Data_IDA.wiff";
wFile = fileControl.GetFileObject(filePath, 1);
long numSamples = wFile.GetNumberOfSamples();

我得到了正确数量的样本。

在我的 C++ dll 中,我有这个(删除了一些 HRESULT 检查以使代码更短):

std::string filePath = "C:\\Data\\April 4\\Data_IDA.wiff";
_bstr_t fileName(filePath.c_str());
IFMANFilePtr ipFMANFile;
IFMANFileControlPtr ipFMANFileControl;
hr = ipFMANFileControl.CreateInstance(__uuidof(FMANFileControl));
hr = ipFMANFile.CreateInstance(__uuidof(FMANFile));
ipFMANFile = ipFMANFileControl->GetFileObject(fileName, 1);
long numSamples = ipFMANFile->GetNumberOfSamples(); 

但文件未正确打开,导致样本为零。

使用 oleview 我查看了 typelib,它对函数说:

[id(0x00000001), helpstring("method GetWiffFileObject")] 
IFMANWiffFile* GetWiffFileObject( [in] BSTR WiffFileName, [in] long sample); 

我从中获取信息的文件是在实验期间写入的文件,就在它获取更多数据之前,它调用我的方法,我应该能够获取最新的文件。在 C# dll 中这是可能的,但在 C++ dll 中则不然。虽然我意识到这是隐藏的细节,但我想知道是否有人知道为什么使用相同接口的 C++ COM dll 和 C#、comvisible dll 在被同一个 dll 调用时会表现出不同的行为。

此刻我很困惑,所以任何想法都会受到赞赏,即使它们被证明是离谱的。如果有人认为他们可以提供帮助,我可以分享我的源代码。

编辑: 我尝试了回答 1 的解决方案,但是我无法编译我的代码。在阅读有关此内容时,我发现了这篇文章: Differences between [in, out] and [out, retval] in COM IDL definitions 这似乎表明,由于 FMANFile 指针被标记为 [out, retval],因此该方法变为:

IFMANFilePtr ExploreData::IFMANFileControl(BSTR filename, long sample);

还是我误解了那篇文章?

编辑 2: 得到它的工作虽然我不太确定为什么。 最初我将头文件中声明的变量作为类的私有成员变量,如下所示:

class ATL_NO_VTABLE CUserIDA :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CUserIDA, &CLSID_UserIDAObject>,
public IUserIDA

.
.
.
public:
STDMETHOD(GetSwitchCriteria)(DOUBLE* intensity, DOUBLE* minMass, DOUBLE* maxMass, VARIANT_BOOL *selectIntensity, LONG* numOfDepCycles);
    .
    .
    .
private:
    ExploreDataObjects::IFMANWiffFilePtr ipFMANWiffFile;
ExploreDataObjects::IFMANWiffFile2Ptr ipFMANWiffFile2;
;

只是为了尝试一下,我将它们移到了类声明的顶部,如下所示:

class ATL_NO_VTABLE CUserIDA :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CUserIDA, &CLSID_UserIDAObject>,
public IUserIDA


ExploreDataObjects::IFMANWiffFilePtr ipFMANWiffFile;
ExploreDataObjects::IFMANWiffFile2Ptr ipFMANWiffFile2;

我认为默认情况下这些也是私有成员,并且和以前一样,所以我无法解释为什么这似乎有效。有人可以解释一下吗?

【问题讨论】:

【参考方案1】:

您的 C++ 代码是正确的,但以下行除外:

hr = ipFMANFile.CreateInstance(__uuidof(FMANFile));

它没有任何意义,因为 ipFMANFile 在下一条语句中再次初始化。

不幸的是,这个 IDL 声明:

IFMANWiffFile* GetWiffFileObject([in] BSTR WiffFileName, [in] long sample); 

仅限于调试目的,因为它不支持通过 HRESULT 报告异常的本机 COM 机制。符合 COM 的声明将是:

HRESULT GetWiffFileObject([in] BSTR WiffFileName, [in] long sample, [out, retval] IFMANWiffFile** fileInstance); 

我相信您无法更改库的代码,因此我建议您在运行 CPP 测试用例时尝试一些外部调试工具,如“procmon.exe”和“dbgview.exe”来检查应用程序事件。查找所有失败的操作。

希望对你有所帮助

【讨论】:

感谢有关这些工具的建议并澄清这一点。我现在会尝试使用它们。

以上是关于为啥我的 C# 和 C++ dll 表现出不同的行为?的主要内容,如果未能解决你的问题,请参考以下文章

在不同的返回类型上将字符串从 C# 传递到 C++ DLL 不同的文本编码

为啥 C# 需要注册 COM DLL 才能引用它?

在 C#(WPF) 中使用 C++ dll

如何将 C++ dll 连接到 C# 项目?

C#:加载 C++ DLL 时出现问题

为啥dll不能在c++中使用?