使用 PInvoke 将句柄传递给托管对象时出现问题
本文关键字:对象 问题 PInvoke 句柄 使用 | 更新日期: 2023-09-27 18:31:18
我对如何将托管对象的句柄从 .Net 传递到非托管代码感到非常困惑。现在我正在使用C#为Oracle Siebel CRM开发一种"驱动程序"。以及我如何面对如何将手柄传递给的问题驱动程序 API 有这样的方法:
ISCAPI ISC_RESULT CreateISCDriverInstance
/* in */(const ISC_STRING mediaTypeStr,
/* in */ const ISC_STRING languageCode,
/* in */ const ISC_STRING connectString,
/* in */ const struct ISC_KVParamList* datasetParams,
/* out */ ISC_DRIVER_HANDLE* handle);
而且我对最后一个参数 ISC_DRIVER_HANDLE* 句柄有问题。我想说它可能看起来很奇怪,但我没有ISC_DRIVER_HANDLE类型的定义。
据我所知,可以使用GCHandle来处理这个...以下是我对如何实施它的看法:
[STAThread]
[DllExport("CreateISCDriverInstance", CallingConvention = CallingConvention.StdCall)]
public static int CreateIscDriverInstance([MarshalAs(UnmanagedType.LPWStr)] string mediaTypeStr,
[MarshalAs(UnmanagedType.LPWStr)] string languageCode,
[MarshalAs(UnmanagedType.LPWStr)] string connectString,
[In] ref IscKvParamList datasetParams,
out IntPtr handle)
{
... // Here I'm doing something with incoming data
var drvEntryPointHandle = GCHandle.Alloc(EntryPoints, GCHandleType.Pinned);
handle = GCHandle.ToIntPtr(drvEntryPointHandle);
return (int) ScErrorCode.ScEcOk;
}
但是,调用此方法后,我将得到CLR崩溃:(我对跟踪量感到非常抱歉)
ModLoad: C:'Debug'DriverLibrary.dll
ModLoad: C:'Windows'SysWOW64'MSCOREE.DLL
ModLoad: C:'Windows'Microsoft.NET'Framework'v4.0.30319'mscoreei.dll
ModLoad: C:'Windows'Microsoft.NET'Framework'v4.0.30319'clr.dll
ModLoad: C:'Windows'SysWOW64'MSVCR120_CLR0400.dll
(730.4a4): Unknown exception - code 04242420 (first chance)
ModLoad: C:'Windows'assembly'NativeImages_v4.0.30319_32'mscorlib'd1265d6159ea876f9d63ea4c1361b587'mscorlib.ni.dll
ModLoad: 'DriverLibrary.dll
ModLoad: C:'Windows'Microsoft.NET'Framework'v4.0.30319'clrjit.dll
ModLoad: C:'Windows'Microsoft.NET'Framework'v4.0.30319'diasymreader.dll
ModLoad: NLog.dll
.......................................
ModLoad: C:'Windows'assembly'NativeImages_v4.0.30319_32'System.Runteb92aa12#'ad1a5e8488b493088c4317191604dc81'System.Runtime.Serialization.ni.dll
(730.4a4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
问题是:如何将托管对象的句柄从 .Net 传递到非托管应用程序?附言附加信息。缺乏信息是这里的主要问题。Oracle 提供的文档非常混乱且不完整。如果有人对应用于信息检索的努力有一些疑问......这是原始文档 https://docs.oracle.com/cd/E14004_01/books/PDF/SiebCTIAdm.pdf 的链接(从 260p 开始)。
但我有一些例子,大约10年前在德尔斐写的。我将从那里提供一些我认为可能有用的代码。1) 函数创建ISCDriverInstance(在TicISCCommunicationDriver类中)
function CreateISCDriverInstance(const AMediaTypeStr, ALanguageCode,
AConnectString: PWideChar; const AParams: TISCNamedParamList;
out ADriverHandle: THandle): HRESULT;
begin
try
ADriverHandle := TicISCCommunicationDriver.CreateInstance(AParams).Handle;
Result := SC_EC_OK;
except
Result := SC_EC_DRIVER_CREATION_ERR;
end;
end;
2) 部分或 TicISC 通信驱动程序防御:
TicISCCommunicationDriver = class(TObject)
strict private
class var
FInstance: TicISCCommunicationDriver;
...
var
FHandle: THandle;
...
3) 构造函数: 类函数 TicISCCommunicationDriver.CreateInstance(const AParams: TISCNamedParamList): TicISCCommunicationDriver; 开始 如果未分配(FInstance),则 FInstance := TicISCCommunicationDriver.Create(AParams); 结果 := 财务; 结束;
constructor TicISCCommunicationDriver.Create(const AParams: TISCNamedParamList);
var
IsDebug: boolean;
begin
inherited Create;
FHandle := THandle(@Self);
...
end;
我从未在 Delphi 中开发过任何东西,但是,据我所知 - 它是在 Delphi 上实现的单例模式。FHandle:THandle
它只是TicISCCommunicationDriver实例的句柄。在谷歌中搜索后,我发现THandle是一个用于标识全局分配的动态内存对象的句柄。附言我也试图使用 HandleRef 找到解决方案,但它也没有帮助。
GCHandle
没有该参数的位置。这用于固定托管内存等操作。此句柄由非托管代码提供。它是一个不透明的指针。
德尔菲代码,顺便说一下,我从你的另一个问题中知道它有点不稳定,宣布这是一个THandle
.这在语义上是错误的,因为我认为这个句柄不是真正的 Win32 HANDLE
。
但是,可以肯定地说,这个手柄只是一个IntPtr
。您完全按照我在上一个问题中所述处理该参数:
out IntPtr handle
该函数为其状态(函数刚刚创建的内容)生成句柄。您记住它,然后将其传递给需要该句柄的其他函数。
经过调查,我发现这里的句柄是指向类实例的指针(其中定义了CreateISCDriverInstance)。将句柄传递给 Siebel 后,它将尝试通过引用调用驱动程序类的方法。
class Driver
{
private static readonly GCHandle gHandle;
static Driver
{
// ......
gHandle = GCHandle.Alloc(DriverEntryPoints.Instance, GCHandleType.Pinned);
}
[STAThread]
[DllExport("CreateISCDriverInstance", CallingConvention = CallingConvention.Cdecl)]
public static int CreateIscDriverInstance([MarshalAs(UnmanagedType.LPWStr)] string mediaTypeStr,
[MarshalAs(UnmanagedType.LPWStr)] string languageCode,
[MarshalAs(UnmanagedType.LPWStr)] string connectString,
[In] ref IscKvParamList datasetParams,
out IntPtr handle)
{
//...
handle = GCHandle.ToIntPtr(gHandle);
//...
}
}
附言 同样调用转换的是 Cdecl。