【问题标题】:Trouble with passing handle to managed object using PInvoke使用 PInvoke 将句柄传递给托管对象的问题
【发布时间】:2014-11-21 13:24:18
【问题描述】:

我很困惑如何将句柄从 .Net 传递给我的托管对象到非托管代码。现在我正在使用 C# 为 Oracle Siebel CRM 开发一种“驱动程序”。以及我如何面对如何将句柄传递给 Driver 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 传递到非托管应用程序? p.s.附加信息。 缺乏信息是这里的主要问题。 Oracle 提供的文档非常混乱且不完整。 如果有人对应用于信息检索的努力有一些疑问...这里是原始文档https://docs.oracle.com/cd/E14004_01/books/PDF/SiebCTIAdm.pdf 的链接(从 260p 开始)。

但我有一些大约 10 年前用 Delphi 编写的示例。 我将从那里提供一些我认为可能有用的代码。 1) 函数 CreateISCDriverInstance(在 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) Part 或 TicISCCommunicationDriver 定义:

TicISCCommunicationDriver = class(TObject)
  strict private
    class var
      FInstance: TicISCCommunicationDriver;
      ...
    var
      FHandle: THandle;
      ...

3) 构造函数: 类函数 TicISCCommunicationDriver.CreateInstance(const AParams: TISCNamedParamList): TicISCCommunicationDriver; 开始 如果未分配(FInstance)则 FInstance := TicISCCommunicationDriver.Create(AParams); 结果:= FInstance; 结束;

constructor TicISCCommunicationDriver.Create(const AParams: TISCNamedParamList);
var
  IsDebug: boolean;
begin
  inherited Create;
  FHandle := THandle(@Self);
    ...
   end;

我从未在 Delphi 中开发过任何东西,但是,据我所知 - 它是在 Delphi 上实现的单例模式。 FHandle:THandle 它只是 TicISCCommunicationDriver 实例的句柄。 在 Google 中搜索后,我发现 THandle 它是一个标识全局分配的动态内存对象的句柄。 pps 我也尝试使用 HandleRef 找到解决方案,但也无济于事。

【问题讨论】:

  • 你必须做到out IntPtr handle。缺少的out 足以使本机代码崩溃。
  • 谢谢你,汉斯!我已经添加了修饰符。但这没有帮助。我遇到了同样的异常: ModLoad: 55010000 552be000 C:\Windows\assembly\NativeImages_v4.0.30319_32\System.Runteb92aa12#\ad1a5e8488b493088c4317191604dc81\System.Runtime.Serialization.ni.dll (22a8.1ba8): 访问冲突 - 代码c0000005(第一次机会)在任何异常处理之前报告第一次机会异常。可能会出现此异常并进行处理。
  • 避免假设您只有一个错误。 Siebel 程序员在设计那个 api 时可能喝醉了。
  • 是的,他们在开发软件和编写文档时一直喝醉了。但是我必须以某种方式处理这个问题:) 我假设问题出在句柄上,基于以下驱动程序加载过程的描述: 加载通信驱动程序的过程如下: 1 通道管理器调用驱动程序句柄方法APIVersion,检查驱动的API版本。
  • 2 通道管理器调用驱动程序句柄方法CreateISCDriverInstance,创建驱动程序实例。 3 通道管理器调用驱动程序句柄方法 RequestCommandEventList,以检索该驱动程序支持的事件和命令列表。我有来自我的 DLL 和 Siebel 的跟踪,我看到应用程序在调用 CreateISCDriverInstance 之后和调用 RequestCommandEventList 之前崩溃。

标签: c# pinvoke unmanaged-memory


【解决方案1】:

GCHandle 没有该参数的位置。这用于固定托管内存等操作。此句柄由非托管代码提供。它是一个不透明的指针。

Delphi 代码,顺便说一下,我从你的另一个问题中知道有点奇怪,将其声明为 THandle。这在语义上是错误的,因为我不认为这个句柄真的是 Win32 HANDLE

但是,可以肯定地说,这个句柄只是一个IntPtr。您完全按照我在上一个问题中的说明处理该参数:

out IntPtr handle

函数产生一个句柄,指向它的状态,指向函数刚刚创建的东西。您记住它,然后将其传递给需要该句柄的其他函数。

【讨论】:

  • 所以...就我而言,我只需要创建一些 IntPtr 并存储在我的驱动程序对象中。然后将瘦 IntPtr 传递给 CRM 应用程序。我想我明白了,对吗?
  • 大卫,很抱歉打扰你。我认为你是对的。但是我仍然遇到应用程序崩溃的问题。让我向您展示调试器日志:2.firepic.org/2/images/2014-11/24/bmilwg9u0mqc.png 我可以在这里找到问题所在。也许你会好心地告诉我我必须朝哪个方向看。谢谢!
  • 我没有更多的想法。对不起。你和甲骨文谈过吗?
  • 是的,我已经向 Oracle 支持部门写了服务请求,但他们还没有回复
【解决方案2】:

经过调查,我发现这里的句柄是指向类实例的指针(其中定义了 CreateISCDriverInstance)。将句柄传递给 Siebel 后,它将尝试通过引用调用 Driver 类的方法。

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 也调用转换。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多