I have a c# code which captures a fingerprint from a device. The code for capturing is written in c++ dll which returns the image through callback side by side to give live view. But the problem is that the application stops working after some time during capture. This happens because of callback method only. If I comment the code in callback method it works fine.
I want to know if the callback method can be put in another thread so that the coming images do not get crashed in callback method.
Following is the way how I have called the method.
/* c# code: */
[DllImport("abc.dll", EntryPoint = "capture", CharSet = CharSet.Auto, ExactSpelling = false, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
static extern int capture(callback liveImageCallback);
private delegate void callback(IntPtr img);
void capture()
{
int ret= capture(callBackLiveImage);
}
public void callBackLiveImage(IntPtr imgPointer)
{
byte[] rawData = new byte[102400];
Marshal.Copy(imgPointer, rawData, 0, 102400);
}
/* c++ code: */
typedef void (__stdcall * Callback)(unsigned char* img);
Callback LiveImageHandler;
void capture(liveCallback)
{
LiveImageHandler = liveCallback;
someMethod();
}
someMethod()
{
LiveImageHandler(raw_img);
}
You have no error handling in the callBackLiveImage method. You're always copying 102400. My unmanaged sense is seeing an unhandled exception like AccessViolationException or such.
Try using a try-catch statement to catch the exception, and log it:
public void callBackLiveImage(IntPtr imgPointer)
{
try
{
byte[] rawData = new byte[102400];
Marshal.Copy(imgPointer, rawData, 0, 102400);
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
Unhandled .NET exceptions in unmanaged code don't really sound like a good idea :)
The specific exception will give you a better idea of what you're dealing with. It might be that the imgPointer is IntPtr.Zero, or it might be that there's less data than you expected. Also, are you sure the C++ library is taking care of disposing of the memory?
Related
I need to communicate with a program from my C# code. I have received a communication library from the company that made the program, along with a quite detailed explanation on how to integrate with the application. I feel I have done everything correctly, the C++ functions all return success statusses, but the most important part, the callback function, is never invoked.
I cannot use [DllImport(...)], I have to use LoadLibrary, GetProcAddress and Marshal.GetDelegateForFunctionPointer to load the library into memory and call it's functions. This is so I keep an instance of the library in memory, so it remembers the callback function. This is because the callback function is registered and then used by subsequent requests to communicate additional information.
I'm keeping a reference to the callback function so the GC doesn't collect it and it isn't available for the C++ code.
When I wait for the callback, nothing happens. I don't get errors, I don't see anything specific happening, it just lets me wait forever. The callback doesn't happen.
I'm hoping that somebody can tell me what I have missed, because I don't see the solution anymore.
class Program
{
private static Callback keepAlive;
static void Main(string[] args)
{
try
{
var comLib = LoadLibrary(#"C:\BIN\ComLib.dll");
var isInstalledHandle = GetProcAddress(comLib, nameof(IsInstalled));
var isInstalled = Marshal.GetDelegateForFunctionPointer<IsInstalled>(isInstalledHandle);
var isInstalledResult = isInstalled("activation-key"); // returns success
Console.WriteLine("Is installed result: " + isInstalledResult);
var registerCallbackHandle = GetProcAddress(comLib, nameof(RegisterCallback));
var registerCallback = Marshal.GetDelegateForFunctionPointer<RegisterCallback>(registerCallbackHandle);
keepAlive = CallbackFunc;
var registerResult = registerCallback(keepAlive); // returns success
Console.WriteLine("Register result: " + registerResult);
var initHandle = GetProcAddress(comLib, nameof(Init));
var init = Marshal.GetDelegateForFunctionPointer<Init>(initHandle);
var initResult = init("ERP_INTEGRATION", "activation-key"); // returns success
Console.WriteLine("Init result: " + initResult);
Console.WriteLine("Wait...");
Console.ReadLine();
FreeLibrary(comLib);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static int CallbackFunc(int id, string data)
{
Console.WriteLine($"{id} > {data}");
return 0;
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int IsInstalled(string registryKey);
private delegate int Callback(int id, string data);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int RegisterCallback(Callback callback);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int Init(string id, string command);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);
}
EDIT 1: I got back from the developers of the software. Apparently, because I'm doing all the work on the main thread, the app can't call back because the thread is busy. I should offload the app (and the callback method) to another thread so the app is free to call the callback funcion. Unfortunately, I have no idea how to do this.
EDIT 2: On request, I put the code in a WinForms app and the callback works nicely there. I do not fully understand why, except that the callback must happen while a) the main thread is free (waiting for input from the form) or b) it offloads the callback to another thread. I have no idea how to simulate either in a console app.
I found an answer that is a little hacky, but it works. I converted my console application to WinForms application. This gives me access to System.Windows.Forms.Application.DoEvents. That allows me to start up a Task which runs out of process and keeps refreshing the events that need to be handled. This allows the external program to invoke the callback function.
private bool _callbackOccured = false;
static async Task Main(string[] args)
{
try
{
var comLib = LoadLibrary(#"C:\BIN\ComLib.dll");
await Task.Run(async () => {
var isInstalledHandle = GetProcAddress(comLib, nameof(IsInstalled));
var isInstalled = Marshal.GetDelegateForFunctionPointer<IsInstalled>(isInstalledHandle);
var isInstalledResult = isInstalled("activation-key"); // returns success
Console.WriteLine("Is installed result: " + isInstalledResult);
var registerCallbackHandle = GetProcAddress(comLib, nameof(RegisterCallback));
var registerCallback = Marshal.GetDelegateForFunctionPointer<RegisterCallback>(registerCallbackHandle);
keepAlive = CallbackFunc;
var registerResult = registerCallback(keepAlive); // returns success
Console.WriteLine("Register result: " + registerResult);
var initHandle = GetProcAddress(comLib, nameof(Init));
var init = Marshal.GetDelegateForFunctionPointer<Init>(initHandle);
var initResult = init("ERP_INTEGRATION", "activation-key"); // returns success
Console.WriteLine("Init result: " + initResult);
while (!_callbackOccured)
{
await Task.Delay(100);
Appliction.DoEvents();
}
FreeLibrary(comLib);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static int CallbackFunc(int id, string data)
{
Console.WriteLine($"{id} > {data}");
_callbackOccured = true;
return 0;
}
Edit: it didn't fully work with Task.Run, I had to use an actual Thread.Start to get it to work correctly. A Task got killed too quickly and I still had the same problem that the external app was closed before it got to do anything usefull, but with a Thread it works as expected.
see answer to this: C# C++ Interop callback. Try converting your delegate to a native function pointer before you pass it to the library.
My C# app needs to talk with a DLL written in C++. I don't have the code of this DLL, but I have the code for a demo app (also in C++) that uses the DLL and works.
Here's the interesting code from the C++ demo app. It has two functions, one of which accepts a callback set of 4 delegates.
typedef BOOL (CBAPI* OPENSCANNERSDK)(HWND hwnd, TCallBackSet* callbackSet, wchar_t* configPath);
typedef void (CBAPI* CLOSESCANNERSDK)();
typedef struct TCallBackSet
{
TOnScannerStatusEvent scannerStatusEvent;
TOnScannerNotifyEvent scannerNotifyEvent;
TOnRFIDStatusEvent rfidStatusEvent;
TOnRFIDNotifyEvent rfidNotifyEvent;
} TCallBackSet;
typedef void (cdecl* TOnScannerStatusEvent ) (int scannerStatus);
typedef void (cdecl* TOnScannerNotifyEvent) (int scannerNotify, int lParam);
typedef void (cdecl* TOnRFIDStatusEvent ) (int rfidStatus);
typedef void (cdecl* TOnRFIDNotifyEvent ) (int rfidnotify);
So, I have to call OpenScannerSDK, pass a callback set with pointers to some functions, do some stuff, and finally call CloseScannerSDK.
I have declared this in my C# app like so:
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_OpenScannerSDK")]
extern public static bool OpenScannerSDK(IntPtr hwnd, TCallBackSet callbackSet,
[MarshalAs(UnmanagedType.LPWStr)]string configPath);
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_CloseScannerSDK")]
extern public static void CloseScannerSDK();
[StructLayout(LayoutKind.Sequential)]
public class TCallBackSet
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TOnScannerStatusEvent(int scannerStatus);
[MarshalAs(UnmanagedType.FunctionPtr)]
public TOnScannerStatusEvent ScannerStatusEvent;
(I have removed the other 3 callbacks for brevity)
}
Finally I use the library like so:
var callback = new TCallBackSet() { ... set the delegates ... }
OpenScannerSDK(IntPtr.Zero, callback, ".");
... do other stuff...
CloseScannerSDK();
All this seems to work - both OpenScannerSDK and CloseScannerSDK and all the others I use between them work correctly. The problem is that as soon as the application tries to exit, I get an APPCRASH in KERNELBASE.dll. I don't see any relevant information in the crash report file. I have noticed that if I do not call OpenScannerSDK, but just some other functions of the DLL that are not related to delegates, the APPCRASH doesn't happen.
I also tried GC.KeepAlive for the delegates, no effect.
I had a similar issue porting other C DLL to C# some years ago.
Function pointers in C# are represented as delegates. Internally, delegates are class instances and they're collected by GC the same way other objects are collected.
Probably in your DLL there's some method that "stops" the API, making it stopping invoking the C++ function pointers. You must call this before you close the application.
Probably your app collects the delegate objects and when the C++ DLL tries to invoke from unmanaged code, finds an invalid object reference.
It's interesting to keep the references in C# of these delegates in private fields, to avoid them to be collected when the application is running. The sympton of this issue is that the application crashes intermitently.
Hope this helps.
To anyone interested, here is the final working solution.
First, CallBackSet is defined like so:
[StructLayout(LayoutKind.Sequential)]
public class CallBackSet
{
public IntPtr ScannerStatusEvent;
public IntPtr ScannerNotifyEvent;
public IntPtr RfidStatusEvent;
public IntPtr RfidNotifyEvent;
}
And then:
OnScannerStatusEvent onScannerStatus = (ScannerStatus status) => {...};
OnScannerNotifyEvent onScannerNotify = (ScannerNotify notify, short lparam) => {...};
OnRfidStatusEvent onRfidStatus = (RfidStatus status) => {...};
OnRfidNotifyEvent onRfidNotify = (RfidNotify notify) => {...};
GCHandle.Alloc(onScannerStatus);
GCHandle.Alloc(onScannerNotify);
GCHandle.Alloc(onRfidStatus);
GCHandle.Alloc(onRfidNotify);
callbackSet = new CallBackSet()
{
RfidNotifyEvent = Marshal.GetFunctionPointerForDelegate(onRfidNotify),
RfidStatusEvent = Marshal.GetFunctionPointerForDelegate(onRfidStatus),
ScannerNotifyEvent = Marshal.GetFunctionPointerForDelegate(onScannerNotify),
ScannerStatusEvent = Marshal.GetFunctionPointerForDelegate(onScannerStatus)
};
callbackPtr = Marshal.AllocHGlobal(Marshal.SizeOf(callbackSet));
Marshal.StructureToPtr(callbackSet, callbackPtr, false);
OpenScannerSDK(IntPtr.Zero, callbackPtr, ".");
Upon further testing I found that actually OpenScannerSDK always returns True, which made me believe it was working correctly, but it may not be the case. I did several tests, including passing a TCallBackSet struct that does not have any members and OpenScannerSDK still returned True.
So it seems the problem is with TCallBackSet and these delegates. Something's wrong there. I tried passing the CallBackSet w and w/o ref, no difference. Noticed int in C++ is not the same as int in C#, so changed functions signatures to use short instead of int. None of these made any difference.
When I run the code below for the GetWindowText I get the following error thrown as an inner Exception:
{"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."}
[DllImport("user32.dll", EntryPoint = "GetWindowTextLength", SetLastError = true)]
internal static extern int GetWindowTextLength(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "GetWindowText", SetLastError = true)]
internal static extern int GetWindowText(IntPtr hwnd, ref StringBuilder wndTxt, int MaxCount);
try{
int strLength = NativeMethods.GetWindowTextLength(wndHandle);
var wndStr = new StringBuilder(strLength);
GetWindowText(wndHandle, ref wndStr, wndStr.Capacity);
}
catch(Exception e){ LogError(e) }
I have 2 questions:
Why is the Error not being caught by the try catch?
Any idea how I can stop the program crashing when it hits this type of error other than using try/catch
Cheers
1.
There are some exceptions that cannot be caught. One type is StackOverflow or OutOfMemory because there is literally no memory to allocate for the handler to run. Another type is one delivered to the CLR via the windows OS. This mechanism is called structured exception handling. These kinds of exceptions can be very bad because the CLR cannot be sure that its own internal state is consistent and are sometimes called corrupted state exceptions. In .Net 4, managed code does not handle these exceptions by default.
The above message is from an AccessViolationException, which is a kind of corrupted state exception. This is happening because you are calling an unmanaged method which is writing past the end of a buffer. See this article on possibly handling these exceptions.
2.
Does the sample code here work? You need to make sure the unmanaged code doesn't write past the end of the StringBuilder's buffer.
public static string GetText(IntPtr hWnd)
{
// Allocate correct string length first
int length = GetWindowTextLength(hWnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
It's possible that calling these external methods is causing problems because of the parameters you're providing to GetWindowText. I think you should try the following:
try{
int strLength = NativeMethods.GetWindowTextLength(wndHandle);
var wndStr = new StringBuilder(strLength + 1);
GetWindowText(wndHandle, wndStr, wndStr.Capacity);
}
catch(Exception e){ LogError(e) }
I am writing a C# app on Windows CE 6 to monitor a 3G modem. The app will call functions in a C DLL to access the modem.
In startup, C# app will call this function to create a new connection:
[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int CreateDataConnection(EVENT_CALLBACK callback);
The EVENT_CALLBACK is defined as:
public delegate void EVENT_CALLBACK(int e, IntPtr data);
A data structure is also defined:
[StructLayout(LayoutKind.Sequential)]
public struct ECIO_INFO
{
public UInt32 ecio1; /*!< Primary scramble code */
public UInt32 ecio2; /*!< Received signal code power */
public UInt32 ecio3; /*!< Energy per chip per power density */
}
In C DLL, a function pointer is passed in CreateDataConnection() for modem status update.
int CreateDataConnection(EVENT_CALLBACK ecb)
{
.
.
fEventCallback = ecb;
// Create a connection
.
.
}
After a connection is created, the DLL will invoke the callback function to update the modem status, for example EC/IO (The ratio of received pilot energy).
Basically, when ECIO changes, the callback function is called to pass the ECIO data to C# app:
In C DLL:
void ProcessNotification(EVENT_CALLBACK fEventCallback)
{
ECIO_INFO ecio_info;
ecio_info.ecio1 = ecio_info.ecio2 = ecio_info.ecio3 = 0;
if(data.nNumOfCells>0)
ecio_info.ecio1 = data.arCellInfo[0].nEcIo;
if(data.nNumOfCells>1)
ecio_info.ecio2 = data.arCellInfo[1].nEcIo;
if(data.nNumOfCells>2)
ecio_info.ecio3 = data.arCellInfo[2].nEcIo;
if(data.nNumOfCells>0)
fEventCallback(ME_RSCP_ECIO, &ecio_info);
}
In C# app, the callback function is defined as:
private void ModemEventCallback(int e, IntPtr data)
{
.
.
Modem.ECIO_INFO new_reinfo = new Modem.ECIO_INFO();
new_reinfo = (Modem.ECIO_INFO)Marshal.PtrToStructure(
data, typeof(Modem.ECIO_INFO));
.
.
}
Now, the problem comes. When the program starts, everything is fine, the connection is created ok and EC/IO is being updated. but after running for several hours, the EC/IO update is stopped. After a test, I found it is stopped when the callback is invoke:
fEventCallback(ME_RSCP_ECIO, &ecio_info);
I don't know what has gone wrong here Probably passing a function pointer in a C# DLL invoke just not right way to do it, or some fault is buried in the code?
Since callback function is just pointer for C/C++, callback parameter must be declared as IntPtr. Create EVENT_CALLBACK instance end ensure that it remains alive all time your program runs. Use Marshal.GetFunctionPointerForDelegate Method to convert delecate instance to IntPtr, and pass resulting IntPtr to CreateDataConnection function.
[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int CreateDataConnection(IntPtr callback);
...
EVENT_CALLBACK c;
c = new EVENT_CALLBACK( ... ); // keep this alive !
...
CreateDataConnection(Marshal.GetFunctionPointerForDelegate(c));
Try this
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void EVENT_CALLBACK(int e, IntPtr data);
It solved my problem.
I think you must use GCHandl.Alloc with GCHandleType.Pinned, by this you will tell gc that this object must remain in memory even though
there might be no "roots" in the application that refer to this object and the memory for this object cannot be compacted
I am interfacing with code that takes a char** (that is, a pointer to a string):
int DoSomething(Whatever* handle, char** error);
Basically, it takes a handle to its state, and if something goes wrong, it returns an error code and optionally an error message (the memory is allocated externally and freed with a second function. That part I've figued out :) ).
I, however, am unsure how to handle in in C#. What I have currently:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern int DoSomething(IntPtr handle, byte** error);
public static unsafe int DoSomething(IntPtr handle, out string error) {
byte* buff;
int ret = DoSomething(handle, &buff);
if(buff != 0) {
// ???
} else {
error = "";
}
return ret;
}
I've poked around, but I can't figure out how to turn that into a byte[], suitable for feeding to UTF8Encoding.UTF8.GetString()
Am I on the right track?
EDIT: To make more explicit, the library function allocates memory, which must be freed by calling another library function. If a solution does not leave me with a pointer I can free, the solution is unacceptable.
Bonus question: As implied above, this library uses UTF-8 for its strings. Do I need to do anything special in my P/Invokes, or just use string for normal const char* parameters?
You should just be able to use a ref string and have the runtime default marshaller take care of this conversion for you. You can hint the char width on the parameter with [MarshalAs(UnmanagedType.LPStr)] to make sure that you are using 8-bit characters.
Since you have a special deallocation method to call, you'll need to keep the pointer, like you've already shown in your question's example.
Here's how I'd write it:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern int DoSomething(
MySafeHandle handle, void** error); // byte** should work, too, I'm just lazy
Then you can get a string:
var errorMsg = Marshal.PtrToStringAnsi(new IntPtr(*error));
And cleanup:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int FreeMyMemory(IntPtr h);
// ...
FreeMyMemory(new IntPtr(error));
And now we have the marshalled error, so just return it.
return errorMsg;
Also note the MySafeHandle type, which would inherit from System.Runtime.InteropServices.SafeHandle. While not strictly needed (you can use IntPtr), it gives you a better handle management when interoping with native code. Read about it here: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle.aspx.
For reference, here is code that compiles (but, not tested yet, working on that next tested, works 100%) that does what I need. If anyone can do better, that's what I'm after :D
public static unsafe int DoSomething(IntPtr handle, out string error) {
byte* buff;
int ret = DoSomething(handle, &buff);
if(buff != null) {
int i = 0;
//count the number of bytes in the error message
while (buff[++i] != 0) ;
//allocate a managed array to store the data
byte[] tmp = new byte[i];
//(Marshal only works with IntPtrs)
IntPtr errPtr = new IntPtr(buff);
//copy the unmanaged array over
Marshal.Copy(buff, tmp, 0, i);
//get the string from the managed array
error = UTF8Encoding.UTF8.GetString(buff);
//free the unmanaged array
//omitted, since it's not important
//take a shot of whiskey
} else {
error = "";
}
return ret;
}
Edit: fixed the logic in the while loop, it had an off by one error.