How to fix 'EntryPointNotFoundException' - c#

I'm trying to import external c++ methods into my C# code.
I have modified a Windows driver which I'm using to access memory. To invoke the driver, I'm using c++ interface. Finally, to invoke the the interface connecting me to the driver, I use C# code.
The problem I'm facing is that during the runtime, I get following error System.EntryPointNotFoundException: Unable to find an entry point named 'GetTargetPid' in DLL 'API.dll'.
Now, The interface itself consists only of single header file. I thought that maybe that is the problem, however from what I've read online, using single header file even for implementation is perfectly fine.
This is my import in C#
[DllImport("API.dll")]
public static extern IntPtr GetTargetPid();
and here I Invoke the method
IntPtr processID = IntPtr.Zero;
...
ProcessID = GetTargetPid();
So my C# code is nothing special.
Now here is my API.dll
extern "C"
{
...
class CDriver
{
public:
//Handle to the driver
HANDLE hDriver;
//Initialization of the handle
CDriver::CDriver(LPCSTR RegistryPath)
{
hDriver = CreateFileA(RegistryPath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
}
...
__declspec(dllexport)
ULONG_PTR GetTargetPid()
{
if (hDriver == INVALID_HANDLE_VALUE)
return false;
PVOID Id = 0;
ULONG_PTR Result;
DWORD Bytes;
if (DeviceIoControl(hDriver, IO_GET_PROCESS_ID, NULL, NULL,
Id, sizeof(Id), &Bytes, NULL)) {
Result = (ULONG_PTR)Id;
return Result;
}
else
return false;
}
Most of the examples I'v read online are using static methods, is that of any importance? what I need is working import, i think this should be trivial, however I can't figure it out.

You have two problems. First problem __declspec(dllexport) ULONG_PTR GetTargetPid() compiles just fine and exports CDriver::GetTargetPid. You don't want that.
In reading your CDriver code I'm convinced that it's not a singleton. If you really want to P/Invoke:
extern "C" {
__declspec(dllexport)
CDriver *CreateCDriver(LPCSTR RegistryPath)
{
return new CDriver(RegistryPath);
}
__declspec(dllexport)
ULONG_PTR GetTargetPid(CDriver *driver)
{
return driver->GetTargetPid();
}
__declspec(dllexport)
CDriver *DestroyCDriver(CDriver *driver)
{
delete driver;
}
} // extern "C"
Second problem: you are P/Invoking a C function. Need Cdecl declarations in C#:
[DllImport("API.dll", CallingConvention=Cdecl, CharSet=CharSet.????)]
public static extern IntPtr CreateCDriver(string name);
[DllImport("API.dll", CallingConvention=Cdecl)]
public static extern IntPtr GetTargetPid(IntPtr cdriver);
[DllImport("API.dll", CallingConvention=Cdecl)]
public static extern IntPtr DestroyCDriver(IntPtr cdriver);
I can't tell from your code whether you compile ANSI or Unicode; fill in CharSet.???? correctly.
The usage of this stuff is like this:
IntPtr cdriver = null;
try {
cdriver = CreateCDriver("whatever");
var pid = GetTargetPid(cdriver);
// do whatever with pid
} finally {
DestroyCDriver(cdriver);
}
The moment you have to move a cdriver reference off the stack you need Dispose() and Finalize().
internal class CDriver : IDisposable {
private IntPtr cdriver;
public CDriver(string registry)
{
cdriver = CreateCDriver("whatever");
}
public void Dispose()
{
Dispose(true);
GC.SupressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
DestroyCDriver(cdriver);
cdriver = IntPtr.Zero;
}
}

Related

AccessViolationException using unmanaged C++ DLL

I'm trying, for the first time, to use an unmanaged C++ DLL ("res_lib") in a C# application. I've used cppsharp to generate the PInvoke code: for example, one of the functions/methods I'm trying to call is get_system_snapshot. From the .h file, this is defined as
SYS_INT SYS_ERR get_system_snapshot(SNAPSHOT_PARMS* snapshotp);
SYS_INT and SYS_ERR equate to a int32_t. SNAPSHOT_PARMS is
typedef struct SNAPSHOT_PARMS
{
SYS_ULONG size;
SYS_UINT count;
SYS_CHAR serial_no[600];
} SYS_PACK_DIRECTIVE SYS_SNAPSHOT_PARMS;
cppsharp has turned this into the following code snippets:
DllImport
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = CallingConvention.StdCall,
EntryPoint="get_system_snapshot")]
internal static extern int GetSystemSnapshot(IntPtr snapshotp);
Object
public unsafe partial class SNAPSHOT_PARMS : IDisposable
{
[StructLayout(LayoutKind.Explicit, Size = 608)]
public partial struct __Internal
{
[FieldOffset(0)]
internal uint size;
[FieldOffset(4)]
internal uint count;
[FieldOffset(8)]
internal fixed sbyte serial_no[600];
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.ThisCall,
EntryPoint="??0SNAPSHOT_PARMS##QAE#ABU0##Z")]
internal static extern global::System.IntPtr cctor(global::System.IntPtr instance, global::System.IntPtr _0);
}
}
public SNAPSHOT_PARMS()
{
__Instance = Marshal.AllocHGlobal(sizeof(global::res_lib.SNAPSHOT_PARMS.__Internal));
__ownsNativeInstance = true;
NativeToManagedMap[__Instance] = this;
}
Main code
static void Main(string[] args)
{
SNAPSHOT_PARMS p = new SNAPSHOT_PARMS();
var result = res_lib.res_lib.GetSystemSnapshot(p);
}
public static unsafe int GetSystemSnapshot(global::res_lib.SNAPSHOT_PARMS snapshotp)
{
var __arg0 = ReferenceEquals(snapshotp, null) ? global::System.IntPtr.Zero : snapshotp.__Instance;
var __ret = __Internal.GetSystemSnapshot(out __arg0);
return __ret;
}
When calling the function, I get the infamous:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I've tried changing the CallingConvention from StdCall to Cdecl, introducing [In] and [Out] to the DllImport etc, but all to no avail. Can anyone see anything obviously wrong with the code - as may be apparent, this is all new to me, and perhaps I'm asking a bit much for cppsharp to generate code that won't need tweaked.
EDIT The original C++ documentation has an example, where the struct is initialised by
#define INIT_STRUCT(struct_p) { memset(struct_p, 0, sizeof(*(struct_p))); (struct_p)->size = sizeof(*(struct_p)); }
and is used
SNAPSHOT_PARMS snapshot_parms;
SYS_ERR result;
INIT_STRUCT(&snapshot_parms);
result = get_system_snapshot(&snapshot_parms);
From the C++ declarations, this should suffice:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SNAPSHOT_PARMS {
public int size;
public int count;
public fixed byte serial_no[600];
}
[DllImport("res_lib", EntryPoint = "get_system_snapshot")]
static extern int GetSystemSnapshot(ref SNAPSHOT_PARMS snapshot);
Use as
var s = new SNAPSHOT_PARMS { size = Marshal.SizeOf<SNAPSHOT_PARMS>() };
int result = GetSystemSnapshot(ref s);
// check result, use s

How to convert a C++ class into a .NET object?

We have a C# programmer that wants a .NET object to do all the underlying work. It should basically be a black box with functions and events.
I had written all this with C++ Builder, using non-visual VCL classes and now it seems like I have to make a .NET object out of it.
I need a simple example of how to create a .NET "box" with one function and one event handler and from there I should be able to implement the rest of it. Should I do this in a COM object? What technology should I use?
Example C++ side.
typedef void __fastcall (__closure *TIntEvent)(int Status);
typedef void __fastcall (__closure *TVoidEvent)(void);
typedef void __fastcall (__closure *TResultEvent)(String cmd, int code);
typedef void __fastcall (__closure *TModeEvent)(int mode, int reason);
class TDevice : public TObject {
private:
// properties
String FPortName;
String FDevice;
String FComment;
String FID;
double FBootware;
double FFirmware;
protected:
public:
// properties
__property String PortName = { read=FPortName };
__property String Device = { read=FDevice };
__property String Comment = { read=FComment };
__property String ID = { read=FID };
__property double Bootware = { read=FBootware };
__property double Firmware = { read=FFirmware };
// event function pointers
TModeEvent OnMode;
TIntEvent OnStatus;
TIntEvent OnSensors;
TVoidEvent OnInfo;
TResultEvent OnResult;
// public interface
bool Connect(void);
void Disconnect(void);
void Reset(void);
void Boot(void);
void GetInfo(void);
void GetTag(void);
};
I have removed all the internal stuff and only left exposed functions, events and properties that should be possible to reach from C#.
From this class I need to create a .NET object like this:
MyLib.IDevice.Connect();
MyLib.IDevice.Disconnect();
MyLib.IDevice.Reset();
MyLib.IDevice.Boot();
MyLib.IDevice.GetInfo();
MyLib.IDevice.GetTag();
I also need C# to connect a function to the Event handlers in the C++ class.
MyLib.IDevice.OnMode = CSharpEventHandler1;
MyLib.IDevice.OnStatus = CSharpEventHandler2;
MyLib.IDevice.OnSensors = CSharpEventHandler3;
MyLib.IDevice.OnInfo = CSharpEventHandler4;
MyLib.IDevice.OnResult = CSharpEventHandler5;
These event handlers are called inside the C++ class to fire the events like this:
if(OnMode != NULL)
{
OnMode(FMode,FReason);
}
There are also a few properties, but these are easy to impalement in a COM interface (if this is what we need)...
Since this is written in C++ Builder and C++ builder can write components (for C++ Builder and Delphi, using ActiveX technology), maybe it is possible to convert a C++ Builder component library into a .Net object/component?
EDIT:
To make it even more clear...
The MyLib.IDevice.Connect() is what I want C# to see... The list of functions are the C++ functions as in a .Net object MyLib with an interface IDevice.
So assuming I have created an instance of MyLib.IDevice as Device, I can call Device.Connect(); from C#.
It is hard... And ugly... The simplest solution is probably to create a C interface:
extern "C"
{
__declspec(dllexport) __stdcall TDevice* NewDevice()
{
return new TDevice();
}
__declspec(dllexport) void __stdcall DeleteDevice(TDevice *pDevice)
{
delete pDevice;
}
__declspec(dllexport) bool __stdcall ConnectDevice(TDevice *pDevice)
{
return pDevice->Connect();
}
.. and so on
}
In C#:
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern IntPtr NewDevice();
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern void DeleteDevice(IntPtr pDevice);
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern bool ConnectDevice(IntPtr pDevice);
... and so on
If you are ok with this, we can begin talking about passing delegates... and it will be a pain, trust me :-)
Uff... It was quite long... C++ side it is better if you create a wrapper for your class. This because you are using __fastcall __closure for your events. Both of these modifiers are incompatible with C#, so you "proxy" them in the wrapper.
// __fastcall not handled by C#
typedef void __stdcall (*TIntEventFunc)(int Status);
typedef void __stdcall (*TVoidEventFunc)(void);
typedef void __stdcall (*TResultEventFunc)(const wchar_t *cmd, int code);
typedef void __stdcall (*TModeEventFunc)(int mode, int reason);
class TDeviceWrapper {
public:
// You could even use directly a TDevice Device, depending on how your program works.
// By using a TDevice *, you can attach the wrapper to a preexisting TDevice.
TDevice *PDevice;
TModeEventFunc OnModeFunc;
TIntEventFunc OnStatusFunc;
TIntEventFunc OnSensorsFunc;
TVoidEventFunc OnInfoFunc;
TResultEventFunc OnResultFunc;
void __fastcall OnStatus(int status) {
OnStatusFunc(status);
}
void __fastcall OnResult(String cmd, int code)
{
OnResultFunc(cmd.c_str(), code);
}
};
extern "C" {
__declspec(dllexport) TDeviceWrapper* __stdcall NewDevice()
{
auto pWrapper = new TDeviceWrapper();
pWrapper->PDevice = new TDevice();
return pWrapper;
}
__declspec(dllexport) void __stdcall DeleteDevice(TDeviceWrapper *pWrapper)
{
delete pWrapper->PDevice;
delete pWrapper;
}
__declspec(dllexport) const wchar_t* __stdcall GetPortName(TDeviceWrapper *pWrapper)
{
return pWrapper->PDevice->PortName.c_str();
}
__declspec(dllexport) bool __stdcall Connect(TDeviceWrapper *pWrapper)
{
return pWrapper->PDevice->Connect();
}
__declspec(dllexport) void __stdcall SetStatus(TDeviceWrapper *pWrapper, TIntEventFunc statusFunc) {
pWrapper->OnStatusFunc = statusFunc;
if (statusFunc) {
pWrapper->PDevice->OnStatus = pWrapper->OnStatus;
} else {
pWrapper->PDevice->OnStatus = nullptr;
}
}
__declspec(dllexport) void __stdcall SetResult(TDeviceWrapper *pWrapper, TResultEventFunc resultFunc) {
pWrapper->OnResultFunc = resultFunc;
if (resultFunc) {
pWrapper->PDevice->OnResult = pWrapper->OnResult;
} else {
pWrapper->PDevice->OnResult = nullptr;
}
}
}
Then C#-side you have to create another wrapper :-) This time because when you pass a delegate C#->C++, the .NET creates a "thunk", but if you don't save the delegate somewhere, this "thunk" gets garbage collected. So the easiest solution is normally to create a wrapper class where you can save the used delegates. You can even incapsulate the Dispose() pattern in this wrapper :-)
public class TDeviceWrapper : IDisposable
{
// Fastcall not handled by C#
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TIntEventFunc(int Status);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TVoidEventFunc();
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void TResultEventFunc(string cmd, int code);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TModeEventFunc(int mode, int reason);
IntPtr ptr;
[DllImport("TDevice.dll")]
static extern IntPtr NewDevice();
[DllImport("TDevice.dll")]
static extern void DeleteDevice(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern IntPtr GetPortName(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern void Connect(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern void SetStatus(IntPtr pWrapper, TIntEventFunc statusFunc);
[DllImport("TDevice.dll")]
static extern void SetResult(IntPtr pWrapper, TResultEventFunc resultFunc);
// To prevent the GC from collecting the managed-tounmanaged thunks, we save the delegates
TModeEventFunc modeFunc;
TIntEventFunc statusFunc;
TIntEventFunc sensorsFunc;
TVoidEventFunc infoFunc;
TResultEventFunc resultFunc;
public void Init()
{
ptr = NewDevice();
}
public string PortName
{
get
{
// Important! .NET will try to free the returned
// string if GetPortName returns directly a string.
// See for example https://limbioliong.wordpress.com/2011/06/16/returning-strings-from-a-c-api/
IntPtr ptr2 = GetPortName(ptr);
return Marshal.PtrToStringUni(ptr2);
}
}
public void Connect()
{
Connect(ptr);
}
public void SetStatus(TIntEventFunc statusFunc)
{
this.statusFunc = statusFunc;
SetStatus(ptr, statusFunc);
}
public void SetResult(TResultEventFunc resultFunc)
{
this.resultFunc = resultFunc;
SetResult(ptr, resultFunc);
}
~TDeviceWrapper()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (ptr != IntPtr.Zero)
{
DeleteDevice(ptr);
ptr = IntPtr.Zero;
}
if (disposing)
{
modeFunc = null;
statusFunc = null;
sensorsFunc = null;
infoFunc = null;
resultFunc = null;
}
}
}
Then you can, for example:
public class MyClass
{
public void StatusEvent(int status)
{
Console.WriteLine("Status: {0}", status);
}
public void ResultEvent(string cmd, int code)
{
Console.WriteLine("Resukt: {0}, {1}", cmd, code);
}
}
and
var mc = new MyClass();
using (var wrapper = new TDeviceWrapper())
{
wrapper.Init();
wrapper.SetStatus(mc.StatusEvent);
wrapper.SetResult(mc.ResultEvent);
wrapper.Connect();
}

typedef equivalent in C# to use C++ DLL

I'm trying to call a C++ structure, exported to a DLL, from C#-Code
This is the C++ interface to the Method I want to call:
typedef void *Handle;
typedef void (*Callback)(Info *info);
typedef void (*Timeout)(Info *info);
typedef struct {
WORD port;
WORD flag;
char name[16];
} Info;
__declspec(dllexport) Handle open(Info *info, Callback c,
Timeout timeout);
This article teached me how to declare the info-struct in C#:
[StructLayout(LayoutKind.Explicit,
Pack=1,Size=36)]
public struct Info
{
[FieldOffset(0)]
public ushort port;
[FieldOffset(2)]
public ushort flag;
[FieldOffset(4)]
public char name;
}
Then I would import the Method in C#:
[DllImport ("MyDLL")] private static extern void Handle open(Info
*info, Callback c, Timeout timeout);
Then I'm stucked, because I don't know how to transfer the typedefs of Handle, Callback and Timeout to C#. Any suggestions?
It is quite complex... Try this... It will probably go boom but then you can tell me how it did go boom and I can help you:
public class MyDllhelper
{
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Info
{
public ushort port;
public ushort flag;
public fixed byte name[16];
public unsafe string Name
{
get
{
fixed (byte* ptr = name)
{
IntPtr ptr2 = (IntPtr)ptr;
return Marshal.PtrToStringAnsi(ptr2, 16).TrimEnd('\0');
}
}
set
{
fixed (byte* ptr = name)
{
IntPtr ptr2 = (IntPtr)ptr;
byte[] bytes = Encoding.Default.GetBytes(value);
int length = Math.Min(15, bytes.Length);
Marshal.Copy(bytes, 0, ptr2, length);
ptr[length] = 0;
}
}
}
}
public VoidRefInfoDelegate C { get; set; }
public VoidRefInfoDelegate Timeout { get; set; }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VoidRefInfoDelegate(ref Info info);
[DllImport("MyDLL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr open(ref Info info, VoidRefInfoDelegate c, VoidRefInfoDelegate timeout);
public IntPtr Open(ref Info info, VoidRefInfoDelegate c, VoidRefInfoDelegate timeout)
{
C = c;
Timeout = timeout;
return open(ref info, C, Timeout);
}
}
Note that you'll have to do something like:
public void CMethod(ref MyDllhelper.Info info)
{
Console.WriteLine("C method called");
}
public void TimeoutMethod(ref MyDllhelper.Info info)
{
Console.WriteLine("Timeout method called");
}
var info = new MyDllhelper.Info();
info.Name = "012345678901234567890"; // Use Name, not name!
info.flag = 1;
info.port = 2;
var helper = new MyDllhelper();
IntPtr handle = helper.Open(ref info, CMethod, TimeoutMethod); // Use Open, not open!
You will need to compile the code with the Properties->Build->Allow unsafe code.
There are two or three interesting points in the code: the C array has been converted to a fixed byte array. I've added a getter/setter Name to handle the conversion from Ansi/ASCII to Unicode and back.
The C function has two callback methods (c and timeout). To use them, you need to "save" somewhere C#-side the delegates you'll use, because otherwise the garbage collector will free the delegates, and you'll receive an exception (see for example https://stackoverflow.com/a/6193914/613130). The C and Timeout properties are used for this.
Those typedefs are for callback functions that will be called from the open(..) method that you are calling. Each of those callback functions is going to take a *Info argument. See answers for calling C++ functions containing callbacks in C# for some examples of how those functions are declared. Of course, you're going to need to do more than just declare them; you're going to have to write the code for doing the work in those functions, too.

Will be C++ classes exported to C# deleted by the garbage collector?

The idea is to create and export a wrapper for the C++ class and then use it from C# like following:
First, let us create the C++ class itself:
File : MyClass.cpp
class myclass
{
public:
int funct(int val)
{
return val + 1;
}
~myclass(){}
};
Then, we create a wrapper:
File wrapper.cpp
extern "C" __declspec(dllexport) myclass* expConst()
{
return new myclass();
}
extern "C" __declspec(dllexport) void expDispose(myclass * obj)
{
delete obj;
}
extern "C" __declspec(dllexport) int expfunct(myclass* obj, int val)
{
return obj->funct(val);
}
Now, we come to c#:
public class CsClass : IDisposable
{
//Import the functions from dll
[DllImport("ExportedLib.dll")]
public static extern IntPtr expDispose(IntPtr obj);
[DllImport("ExportedLib.dll")]
public static extern IntPtr expConst();
[DllImport("ExportedLib.dll")]
public static extern int expfunct(IntPtr obj, int val);
IntPtr objPtr;
public CsClass()
{
objPtr = expConst();
}
public int funct(int q)
{
return expfunct(objPtr, q);
}
public void Dispose()
{
expDispose(objPtr);
}
}
Finally, we ecxecute this by
File:Program.cs
class Program
{
static void Main(string[] args)
{
CsClass v = new CsClass();
Console.WriteLine(v.Func(1));
}
}
I tested this simple things and the program printed 2 as it was expected.
The question is, whether the garbage collector of C# will move the created C++ object and, thus, making the objPtr to point to some wrong place in memory?
Are there any other principal obstacles here? I mean some unsolvable problems which make such an approach impossible.
Thanks in advance.
No, the garbage collector only goes to work for managed memory. Your class created in C++ is not managed memory and will not be touched, for good or for bad. You will need to manage it yourself as you already did.

Marshalling PDouble (C# -> Delphi)

I've got an unmanaged DLL file written in Delphi, containing a function with the following definition:
function F(tgran: integer; inputs: PDouble; goutp, outputs, toutp: PDouble): integer; stdcall; external 'mydll.dll';
I've written an Adapter in C# that should help me consume it.
[UnmanagedFunctionPointer(APIAdapter.Convention)]
public delegate int FDelegate(int tgran, IntPtr inputs, IntPtr goutp, IntPtr outputs, IntPtr toutp);
public class APIAdapter : IDisposable
{
public const string DllName = "mydll.dll";
public const CallingConvention Convention = CallingConvention.StdCall;
public FDelegate F;
[DllImport("kernel32")]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32")]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, String procname);
private IntPtr _dllHandle;
public APIAdapter()
{
_dllHandle = LoadLibrary(DllName);
F = (FDelegate)GetFunction<CalcCavSpDelegate>("F");
}
private Delegate GetFunction<T>(string procName)
{
IntPtr procAddress = GetProcAddress(_dllHandle, procName);
return Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T));
}
~APIAdapter()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
}
while (FreeLibrary(_dllHandle))
{
}
}
}
The usage is pretty straightforward:
using(var api = new APIAdapter())
{
// Call API functions
}
The problem is an AccessViolationException that happens within the DLL file.
I've tried to pass the PDouble variables as double[], double* (unsafe code), IntPtr. It's the same story regardless of the method I choose. I've tried to substantially increase the size of the arrays passed in to exclude errors with array indexing - AccessViolation exception again.
What is the proper way of passing a PDouble into an unmanaged Delphi DLL file?
I think you can do away with all the LoadLibrary, GetProcAddress and FreeLibrary complexity by simply using DllImport attribute. Though, I can say that I am not aware about any specific reason of why you chose to go this way.
Anyway, you can simply include ref double doubleArg in your declaration to pass PDouble. There should be no need of IntPtr here.

Categories