使用 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 找到解决方案,但它也没有帮助。

使用 PInvoke 将句柄传递给托管对象时出现问题

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。