我正在调试其他一些程序员的 Windows Media Player 插件源代码。这个插件有时会导致 WMP 崩溃,有时需要很长时间才能打开插件设置窗口。只有在播放音乐时打开设置窗口时才会出现此问题。如果播放器停止,它可以毫无问题地打开。
在查看代码和调试时,我找到了似乎是问题原因的代码行。
属性页有如下成员变量:
CComPtr<IDsp_plugin> m_pDsp_plugin;
并且属性页在初始化时调用COM对象的get_text方法:
unsigned char * txt = NULL;
//m_pDsp_plugin is a valid pointer to IDsp_plugin
HRESULT res = m_pDsp_plugin->get_text(&txt);
此时hres为“0x80010105:服务器抛出异常”。并且 Visual Studio 调试输出显示“wmplayer.exe 中 0x764efbae 的第一次机会异常:0x80010105:
get_text 方法定义如下:
在 Dsp_plugin.idl 中
interface IDsp_plugin : IUnknown
{
HRESULT get_text([out] unsigned char* *pVal);
...
在 Dsp_plugin.h 中
class ATL_NO_VTABLE CDsp_plugin :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CDsp_plugin, &CLSID_Dsp_plugin>,
public IDsp_plugin,
public IMediaObject,
public IWMPPluginEnable,
public ISpecifyPropertyPages
{
STDMETHOD(get_text)(unsigned char* *txt);
...
最后是抛出异常的方法本身: Dsp_plugin.cpp
STDMETHODIMP CDsp_plugin::get_text (unsigned char* *txt)
{
... // some code for copying a valid string from somewhere to char* y
// 11 bytes of memory for y was allocated using malloc(10+1);
// y contains a valid C string here, tested with debugger and passing to OutputDebugStringA
*txt = (unsigned char*)(y); // This line executes normally, but at the end the caller gets "The server threw an exception." and WMP starts behaving weirdly.
// If I comment it out, the caller gets S_OK and there are no any issues with WMP.
return S_OK;
}
COM DLL 是在设置“使用 Unicode 字符集”的情况下编译的。
我不是经验丰富的 COM 程序员,但将字符串作为 unsigned char** 传递对我来说似乎很不寻常,我在处理 COM 时看到的主要是 BSTR 或 VARIANT。
也许某些 COM 大师可以解释,为什么会发生此异常,是否可以通过将方法转换为使用 BSTR* 和 SysAllocString/SysfreeString 而不是 unsigned char**/malloc/free 来修复它?
最佳答案
简单地说,COM 不知道如何传递 unsigned char *
类型的指针。 default marshalling rules被应用(因为接口(interface)定义没有指定任何参数属性),并且,如果我正确地解释这个,COM 正确地编码外部指针本身 txt
,但对待 *txt
作为指向单个 unsigned char
的指针,而不是字符串。
如果调用者和被调用者碰巧在同一间公寓,这可能仍然有效;从它的声音来看,它们不是。
最简单的解决方案就是将参数设为 BSTR *
。 COM 对 BSTR
有特殊处理,这将确保它被正确传递。
关于C++、COM 和传递字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6754081/