MinidumpWriteDump from managed code throws an AccessViolationException - c#

I have the basic MiniDumpWriteDump method interop copied off the internet in my C# (3.5) project.
Up till now, i have used this code to register on the UnhandledException event, to take a crash dump before the process shuts down.
In a particular scenario i am facing now, i have set this function to be used in some other case, to take a diagnostic memory dump of the process.
Whenever this function gets called (not from the UnhandledException handler), it throws an AccessViolationException
Here's what the MiniDump code looks like (removed some redundant parts):
using (var fs = new System.IO.FileStream(fileName,
System.IO.FileMode.Create,
System.IO.FileAccess.Write,
System.IO.FileShare.None))
{
MiniDumpExceptionInformation exp;
exp.ThreadId = GetCurrentThreadId();
exp.ClientPointers = false;
exp.ExceptionPointers = Marshal.GetExceptionPointers();
bool bRet = MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
fs.SafeFileHandle.DangerousGetHandle(),
(uint)dumpType,
ref exp,
IntPtr.Zero,
IntPtr.Zero);
return bRet;
}
Native types are defined like so:
//typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
// DWORD ThreadId;
// PEXCEPTION_POINTERS ExceptionPointers;
// BOOL ClientPointers;
//} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
// Pack=4 is important! So it works also for x64!
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct MiniDumpExceptionInformation
{
public uint ThreadId;
public IntPtr ExceptionPointers;
[MarshalAs(UnmanagedType.Bool)]
public bool ClientPointers;
}

Marshal.GetExceptionPointers() might return IntPtr.Zero (maybe when the OS sees the exception as handled). If this is the case you can try to put the call of 'MiniDumpWriteDump' somewhere else. I have had the same problem and solved it by putting 'MiniDumpWriteDump' into the event handler 'AppDomain.CurrentDomain.FirstChanceException'.

Related

Function works in .NET framework 2.0 but not in any other version

On .NET 5 (and .NET core 3 and 3.1) when debugging after around 5 seconds the code throws System.ExecutionEngineException which seems like it should never pop up as it is something obsolete as far as I understood from searching.
The same code on .NET Framework >2 (e.g 4.8 or 4.7.2) similarly works for around 5 seconds then throws the following exception:
Managed Debugging Assistant 'CallbackOnCollectedDelegate' : 'A callback was made on a garbage collected delegate of type
'SoundCheck!SoundCheck.CHCNetSDK+VOICEDATACALLBACKV30::Invoke'. This
may cause application crashes, corruption and data loss. When passing
delegates to unmanaged code, they must be kept alive by the managed
application until it is guaranteed that they will never be called.'
But on .NET Framework 2 it works magically without any issues.
As far as I understand I should somehow make it so that garbage collection stops and does not collect this method. But I am unfamiliar with this. I do not know how I should approach this.
I am using the Hikvision SDK https://www.hikvision.com/en/support/download/sdk/
The code:
Dll import:
public delegate void VOICEDATACALLBACKV30(int lVoiceComHandle, IntPtr pRecvDataBuffer, uint dwBufSize, byte byAudioFlag, System.IntPtr pUser);
[DllImport(#"..\bin\HCNetSDK.dll")]
public static extern int NET_DVR_StartVoiceCom_V30(int lUserID, uint dwVoiceChan, bool bNeedCBNoEncData, VOICEDATACALLBACKV30 fVoiceDataCallBack, IntPtr pUser);
Start button:
private void btnVioceTalk_Click(object sender, EventArgs e)
{
if (m_bTalk == false)
{
CHCNetSDK.VOICEDATACALLBACKV30 VoiceData = new CHCNetSDK.VOICEDATACALLBACKV30(VoiceDataCallBack);
lVoiceComHandle = CHCNetSDK.NET_DVR_StartVoiceCom_V30(m_lUserID, 1, true, VoiceData, IntPtr.Zero);
if (lVoiceComHandle < 0)
{
iLastErr = CHCNetSDK.NET_DVR_GetLastError();
str = "NET_DVR_StartVoiceCom_V30 failed, error code= " + iLastErr;
MessageBox.Show(str);
return;
}
else
{
btnVioceTalk.Text = "Stop Talk";
m_bTalk = true;
}
}
else
{
if (!CHCNetSDK.NET_DVR_StopVoiceCom(lVoiceComHandle))
{
iLastErr = CHCNetSDK.NET_DVR_GetLastError();
str = "NET_DVR_StopVoiceCom failed, error code= " + iLastErr;
MessageBox.Show(str);
return;
}
else
{
btnVioceTalk.Text = "Start Talk";
m_bTalk = false;
}
}
}
Callback function:
public void VoiceDataCallBack(int lVoiceComHandle, IntPtr pRecvDataBuffer, uint dwBufSize, byte byAudioFlag, System.IntPtr pUser)
{
byte[] sString = new byte[dwBufSize];
Marshal.Copy(pRecvDataBuffer, sString, 0, (Int32)dwBufSize);
if (byAudioFlag ==0)
{
string str = "sound1.pcm";
FileStream fs = new FileStream(str, FileMode.Create);
int iLen = (int)dwBufSize;
fs.Write(sString, 0, iLen);
fs.Close();
}
if (byAudioFlag == 1)
{
string str = "sound2.pcm";
FileStream fs = new FileStream(str, FileMode.Create);
int iLen = (int)dwBufSize;
fs.Write(sString, 0, iLen);
fs.Close();
}
}
From what I understood you declare a delegate type VOICEDATACALLBACKV30
and you have a method implementing that signature. This is method VoiceDataCallBack
In the line CHCNetSDK.VOICEDATACALLBACKV30 VoiceData = new CHCNetSDK.VOICEDATACALLBACKV30(VoiceDataCallBack);
you instantiate that callback inside an eventhandler, so it's managed memory allocation "lives" on the stack, for a short time.
Then you pass it to some SDK function.
Seems the unmanaged SDK functions keeps on working with that delegate, for an obvoiusy longer time and hold an unmanaged reference/pointer to that delegate .
But your .NET code has already garbage collected it after a short while, because it is no longer referenced in managed code.
So when the SDK invokes the callback the first time after its managed memory has been collected, it's crashing.
So need to keep a reference in managed code, by simply assigning it to a field, in order to keep it alive on the heap. So it is not GC'ed.
//declare field
private CHCNetSDK.VOICEDATACALLBACKV30 _voiceData;
....
//inside 'btnVioceTalk_Click' event handler
if (_voiceData == null) {
_voiceData = new CHCNetSDK.VOICEDATACALLBACKV30(VoiceDataCallBack);
}
Likely .NET Framework 2 had another GC implementation/algorithm.

void ** handles with P/Invoke

I am using a C API DLL from a 3rd party vendor. The problem I have is that I can't seem to find a good template for Marshalling the following C code:
API_Open( void ** handle );
API_Close( void * handle );
The calls are simplified, but the handle is a void *, which is (in C) passed into the API_Open call as &handle, and then passed into API_Close as handle.
I've tried to do the same in C#, but can't figure out how to Marshal handle properly. My C# version (latest try) is:
[DllImport("External.dll",EntryPoint="API_Open")]
public static extern int API_Open( out IntPtr handle );
[DllImport("External.dll",EntryPoint="API_Close")]
public static extern int API_Close( IntPtr handle );
public static int Wrapper_API_Open( ref Int32 handle )
{
int rc = SUCCESS;
// Get a 32bit region to act as our void**
IntPtr voidptrptr = Marshal.AllocHGlobal(sizeof(Int32));
// Call our function.
rc = API_Open(out voidptrptr);
// In theory, the value that voidptrptr points to should be the
// RAM address of our handle.
handle = Marshal.ReadInt32( Marshal.ReadIntPtr(voidptrptr) );
return rc;
}
public static int Wrapper_API_Close(ref Int32 handle)
{
int rc = SUCCESS;
// Get a 32bit region to act as our void *
IntPtr voidptr = Marshal.AllocHGlobal(sizeof(Int32));
// Write the handle into it.
Marshal.WriteInt32(voidptr,handle);
// Call our function.
rc = API_Close(voidptr);
return rc;
}
public void SomeRandomDrivingFunction()
{
.
.
.
Int32 handle;
Wrapper_API_Open( ref handle );
.
.
.
Wrapper_API_Close( ref handle );
.
.
.
}
The API return code is always INVALID_DEVICE_OBJECT when I call API_Close. Any thoughts? I thought this would be pretty straightforward, but I'm having trouble wrapping my head around the void** and void* parts of the function calls.
Thanks
You seem to be over-complicating this significantly. I don't know why you want to introduce Int32 for the handles since they do need to be pointer sized. You should use IntPtr.
The API_Open accepts the address of the variable where the handle is returned. The caller allocates that variable and passes it to the callee, which populates the variable. The C function might look like this:
int API_Open(void **handle)
{
*handle = InternalCreateHandle();
return CODE_SUCCESS;
}
You'd call that in C like this:
void *handle;
int retval = API_Open(&handle);
if (retval != CODE_SUCCESS)
// handle error
// go one and use handle
Translated to C#, the void* maps to IntPtr, and the use of the double pointer is just a means to get around the fact that C only supports pass-by-value. In C# you would use pass-by-reference.
For API_Close it is even simpler because we are passing the handle by value:
int API_Close(void *handle)
{
InternalCloseHandle(handle);
return CODE_SUCCESS;
}
And the calling code is simply:
int retval = API_Close(handle);
if (retval != CODE_SUCCESS)
// handle error
So, the C# wrapper functions should be:
public static int Wrapper_API_Open(out IntPtr handle)
{
return API_Open(out handle);
}
public static int Wrapper_API_Close(IntPtr handle)
{
return API_Close(handle);
}
At which point these wrapper methods do look somewhat pointless!

running callback in different thread

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?

Access Violation with P/Invoke SendMessage and Message.GetLParam

I get an intermittent error when calling Message.GetLParam, getting messages sent between processes.
I have two processes, both written in C# (.Net 3.5). I am using the Win32 function SendMessage() to send data from one process (the source) to the other (the target). The target process's main window (it's a Windows Forms app) overrides the WndProc() function to receive messages.
The source process locates the other by using the Process.GetProcessesByName() function, then using the Process.MainWindowHandle to get the window handle that I want to send the message to. The code of the source looks like this:
Process[] procs = Process.GetProcessesByName("MyTargetProcess");
if (procs != null
&& procs.Length > 0)
{
IntPtr win = procs[0].MainWindowHandle;
var someData = new Win32.COPYDATASTRUCT // This is a struct that I defined
{
// Initialize fields of the struct
};
Win32.SendMessage(win,
Win32.MyMsgCode, // my custom message
IntPtr.Zero, // wParam = 0
ref someData);
}
The target process code looks like this:
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == Win32.MyMsgCode)
{
Win32.COPYDATASTRUCT ds;
try
{
ds = (Win32.COPYDATASTRUCT)m.GetLParam(typeof(Win32.COPYDATASTRUCT));
}
catch (Exception ex)
{
log.ErrorFormat("Got exception in WndProc", ex);
}
// Do something with the message
....
}
Win32 is a static class I defined that gets all my P/Invoke definitions.
I do not understand why I am catching an AccessViolationException in WndProc.
Does anybody have an idea why? and why it only happens some of the time?
Thanks for your attention!
-------------------------------- EDIT ------------------------------------------
Another thing that baffles me: the COPYDATASTRUCT is declared as
public static readonly int WM_COPYDATA = 0x004a;
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
// Specifies data to be passed to the receiving application.
public string dwData;
// Specifies the size, in bytes, of the data pointed to by the lpData member.
public int cbData;
// Pointer to data to be passed to the receiving application. This member can be NULL.
public string lpData;
}
It is initialized like this:
string payload = " some data ";
var someData = new Win32.COPYDATASTRUCT // This is a struct that I defined
{
dwData = "bogusData",
cbData = sizeof(char) * payload.Length,
lpData = payload
};
And in target code, I always receive dwData = null.
----------------------- 2nd edit --------------------------------------
I just tried with adding the zero terminator, and I still get the error.
If I change the marshalling code to do my own marshalling as in
IntPtr pcds = Marshal.AllocHGlobal(Marshal.SizeOf(someData));
Marshal.StructureToPtr(someData, pcds, true);
Win32.SendMessage(win, (uint)Win32.WM_COPYDATA, IntPtr.Zero, pcds);
Then the error happens ALL THE TIME! However, if I repeat the GetLParam() call in the catch block, it succeeds almost all the time, on the 2nd try.
The thing that jumps out at me is that cbData is set incorrectly. You need to account for the zero-terminator at the end of the string.
cbData = sizeof(char) * (payload.Length+1)
That would certainly explain the error. When you send the message, the WM_COPYDATA marshalling would not copy the zero terminator and so the recipient would then read beyond the end of the buffer into uninitialized values.
I also wonder about sizeof(char). Are you calling the Unicode version of SendMessage? If not then I'd expect to see access violations in the sending code.
You should also be wary that the recipient application opens itself to buffer overruns. Because it ignores the value of cbData and treats the lpData field as a null-terminated pointer it may be possible for an attacker to force your app to execute arbitrary code. To defend against this, you should copy the data, cbData bytes of it, into a byte array and then convert to a string.
The other issue is that you need to declare dwData as UIntPtr. The way you have defined the struct, the recipient code treats dwData as a pointer to a string which will provoke AVs because you have crossed process boundaries.
I'd also point out that cbData is unsigned and should be uint but that is a benign error here.

Handling exception from unmanaged dll in C#

I have the following function written in C#
public static string GetNominativeDeclension(string surnameNamePatronimic)
{
if(surnameNamePatronimic == null)
throw new ArgumentNullException("surnameNamePatronimic");
IntPtr[] ptrs = null;
try
{
ptrs = StringsToIntPtrArray(surnameNamePatronimic);
int resultLen = MaxResultBufSize;
int err = decGetNominativePadeg(ptrs[0], ptrs[1], ref resultLen);
ThrowException(err);
return IntPtrToString(ptrs, resultLen);
}
catch
{
return surnameNamePatronimic;
}
finally
{
FreeIntPtr(ptrs);
}
}
Function decGetNominativePadeg is in unmanaged dll
[DllImport("Padeg.dll", EntryPoint = "GetNominativePadeg")]
private static extern Int32 decGetNominativePadeg(IntPtr surnameNamePatronimic,
IntPtr result, ref Int32 resultLength);
and throws an exception:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
The catch that is in C# code doesn't actually catch it. Why? How to handle this exception?
Thank you for your help!
"The CLR no longer delivers exceptions for corrupted process state to exception handlers in managed code."
.NET Framework 4 Migration Issues.
Just add this to the config file:
http://msdn.microsoft.com/en-us/library/dd638517.aspx
If the IntPtr result parameter is to receive the value from within the function, it must be marked ref.
I don't see ptrs[1] being assigned any value before passing.
Try changing the definition to:
[DllImport("Padeg.dll", EntryPoint = "GetNominativePadeg")]
private static extern Int32 decGetNominativePadeg(IntPtr surnameNamePatronimic,
**ref** IntPtr result, ref Int32 resultLength);
The reason probably is that it's trying to write to "result" which is marked as input-only.
you have probably turned off debugging of unmanaged code.
"Enable unmanaged code debugging option" must be check in project properties under the Debug section. After this, the exception is shown in the debugging process.

Categories

Resources