Trouble with passing handle to managed object using PInvoke

1.3k views Asked by At

I'm quite confused about how to pass handle to my managed object from .Net to unmanaged code. Now I'm developing kind of "driver" for Oracle Siebel CRM using C#. And how i faced with problem about how to pass handle to Driver API have such method:

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);

And i have the problem with last parameter ISC_DRIVER_HANDLE* handle. I want to say that it may looks quite weird, but i don't have a definition of ISC_DRIVER_HANDLE type.

As far as I know it's possible to use GCHandle to work with this... And here is my vision about how it should be implemented:

    [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; 
}

But, after call of this method i'll get the CLR crash: (i'm terrible sorry about amount of traces)

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.

Question is: How to pass handle to managed object from .Net in to unmanaged application? p.s. Additional info. Lack of information is main problem here. Documentation provided by Oracle are very confusing and incomplete. If anyone have some doubts about efforts that have been applied to information retrieval... Here is a link to original documentation https://docs.oracle.com/cd/E14004_01/books/PDF/SiebCTIAdm.pdf (starts from 260p.).

But I have some example which were written in Delphi about 10 years ago. I'll provide some code from there which I think may be useful. 1) Function CreateISCDriverInstance(in TicISCCommunicationDriver class)

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 or TicISCCommunicationDriver defenition:

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

3) Constructor: class function TicISCCommunicationDriver.CreateInstance(const AParams: TISCNamedParamList): TicISCCommunicationDriver; begin if not Assigned(FInstance) then FInstance := TicISCCommunicationDriver.Create(AParams); Result := FInstance; end;

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

I've never developed anything in Delphi, but, as far as i can get - it's singleton pattern implemented on Delphi. And FHandle:THandle it's just a handle to instance of TicISCCommunicationDriver. After search in Google i found that THandle it's a handle identifying a globally allocated dynamic memory object. p.p.s. Also i tried to find the solution using HandleRef, but it doesn't helped too.

2

There are 2 answers

4
David Heffernan On BEST ANSWER

GCHandle has no place for that parameter. That's used for operations like pinning managed memory. This handle is provided by the unmanaged code. It is an opaque pointer.

The Delphi code, which incidentally I know from your other question to be a little wonky, declares this as a THandle. That's semantically off because I don't think this handle is really a Win32 HANDLE.

However, it is safe to say that this handle is just an IntPtr. You deal with that parameter exactly as I stated in your previous question:

out IntPtr handle

The function yields a handle to its state, to the thing that the function just created. You remember it and then pass it on to the other functions that need that handle.

0
Anton Antonenko On

After investigation, i found that here handle is a pointer to instance of class( where CreateISCDriverInstance is defined). After passing the handle to Siebel it will be trying to call methods of Driver class by reference.

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);
//...
}

}

p.s. Also calling conversions is Cdecl.