COM 互操作和对 C# 中接口指针的指针进行封送处理
我正在尝试在 C# 应用程序中使用 Microsoft 的文本服务框架。到目前为止,一切都很顺利,但我遇到了一些让我难堪的事情。根据 MSDN 文档,ITfFnReconversion 接口发布了此方法:
HRESULT GetReconversion(
[in] ITfRange *pRange,
[out] ITfCandidateList **ppCandList
);
我在 C# 中将其声明为:
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetReconversion([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange, [Out, MarshalAs(UnmanagedType.Interface)] out ITfCandidateList ppCandList);
并且像这样调用:
ITfCandidateList candidateList;
reconversionInstance.GetReconversion(range, out candidateList);
reconversionInstance 的值和范围是之前设置的,我相信它们是有效的。 每次执行此行时,我都会收到访问冲突错误,表明某些内容试图读取或写入受保护的内存。我假设这是由于候选人列表参数的编组不当造成的,但我对其他可能性持开放态度。
鉴于参数被声明为指向指针的指针,我还尝试将其作为 IntPtr 传递,如下所示:
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetReconversion([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr ppCandList);
IntPtr candidateList;
reconversionInstance.GetReconversion(range, out candidateList);
但仍然出现相同的错误。
我怎样才能正确地整理它,以便我可以获得 ITfCandidateList 的实例?
为了澄清起见,以下是我导入的接口,尽管正如我所提到的,我为 GetReconversion 尝试了一些不同的签名:
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("4CEA93C0-0A58-11D3-8DF0-00105A2799B5")]
public interface ITfFnReconversion : ITfFunction
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetDisplayName([Out, MarshalAs(UnmanagedType.BStr)] out string pbstrName);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void QueryRange([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange, [In, Out, MarshalAs(UnmanagedType.Interface)] ref ITfRange ppNewRange, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfConvertable);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetReconversion([In, MarshalAs(UnmanagedType.Interface)] ref ITfRange pRange, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr ppCandList);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Reconvert([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange);
}
[ComImport, Guid("A3AD50FB-9BDB-49E3-A843-6C76520FBF5D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITfCandidateList
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void EnumCandidates([Out, MarshalAs(UnmanagedType.Interface)] out IEnumTfCandidates ppEnum);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetCandidate([In] uint nIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITfCandidateString ppCand);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetCandidateNum([Out] out uint pnCnt);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetResult([In] uint nIndex, [In, ComAliasName("TSF.TfCandidateResult")] TfCandidateResult imcr);
}
I'm attempting to use Microsoft's Text Services Framework in a C# app. So far, it's all gone swimmingly, but I've run into something that has me stumped. According to the MSDN docs, the ITfFnReconversion interface publishes this method:
HRESULT GetReconversion(
[in] ITfRange *pRange,
[out] ITfCandidateList **ppCandList
);
Which I have declared in C# as:
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetReconversion([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange, [Out, MarshalAs(UnmanagedType.Interface)] out ITfCandidateList ppCandList);
And am calling like so:
ITfCandidateList candidateList;
reconversionInstance.GetReconversion(range, out candidateList);
The value of reconversionInstance and range were set earlier and I'm confident that they are valid.
Every time this line executes, I get an Access Violation error indicating that something attempted to read or write protected memory. I'm assuming that this is due to improper marshaling of the candidateList param, but am open to other possibilities.
Given that the param is declared as a pointer to a pointer, I also tried passing it as an IntPtr, like so:
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetReconversion([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr ppCandList);
IntPtr candidateList;
reconversionInstance.GetReconversion(range, out candidateList);
But was left with the same error.
How can I marshal this correctly so that I can obtain an instance of ITfCandidateList?
For clarification, here are the interfaces as I've imported them, though as I mentioned, I have tried a few different signatures for GetReconversion:
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("4CEA93C0-0A58-11D3-8DF0-00105A2799B5")]
public interface ITfFnReconversion : ITfFunction
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetDisplayName([Out, MarshalAs(UnmanagedType.BStr)] out string pbstrName);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void QueryRange([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange, [In, Out, MarshalAs(UnmanagedType.Interface)] ref ITfRange ppNewRange, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfConvertable);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetReconversion([In, MarshalAs(UnmanagedType.Interface)] ref ITfRange pRange, [Out, MarshalAs(UnmanagedType.SysInt)] out IntPtr ppCandList);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Reconvert([In, MarshalAs(UnmanagedType.Interface)] ITfRange pRange);
}
[ComImport, Guid("A3AD50FB-9BDB-49E3-A843-6C76520FBF5D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITfCandidateList
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void EnumCandidates([Out, MarshalAs(UnmanagedType.Interface)] out IEnumTfCandidates ppEnum);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetCandidate([In] uint nIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITfCandidateString ppCand);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetCandidateNum([Out] out uint pnCnt);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetResult([In] uint nIndex, [In, ComAliasName("TSF.TfCandidateResult")] TfCandidateResult imcr);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这里显然有问题。
ITfCandidateList **ppCandList
无法转换为简单输出:这是指向ITfCandidateList
指针的指针。在我看来,您需要将其定义为 IntPtr,然后尝试使用 Marshal.PtrToStructure 读取该部分内存。
There is something obviously wrong here.
ITfCandidateList **ppCandList
cannot translate to a simple out: this is pointer to a pointer ofITfCandidateList
.In my opinion, you need to define that as
IntPtr
and then try to read that part of memory usingMarshal.PtrToStructure.