1

Current situation:
At my current project I have to use an old C++ dll in a C# winforms application to write on a Hardlock licensing dongle.

My application is a simple interface which allows the user to write license information to the dongle. If the dongle already contains data a yes/no dialog is shown to the user by the C++ dll. If the information is successfully written to the dongle the C++ dll shows a confirm dialog.

When our logistics department uses this tool they are going to write a lot of dongles (200 to 800 at a day). So they don't want to confirm message boxes.

While searching a solution to close popups/message boxes of third party libraries I found some examples using platform invoke. After playing around unsuccessfully with the solution in this post How to suppress a dialog box displayed by code that I can't change? I tried the following approach:

Every 500 milliseconds I check if the popup window is shown and confirm it with ENTER.

private void initButton_Click(object sender, EventArgs e)
{
    messageBoxCloseTimer.Interval = 500;
    messageBoxCloseTimer.Start();
    WriteDongle();
}

private void WriteDongle()
{
    // This code shows a message box        
}

private void MessageBoxCloseTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    BeginInvoke(new MethodInvoker(delegate
    {
        var hwnd = FindWindow("#32770", "Information");
        if (hwnd.ToInt32() < 1)    // Window not found
            return;

        SendKeys.Send("{ENTER}");
        messageBoxCloseTimer.Stop();
    }));
}

[DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

// C++ dll which is used to write the dongle
[DllImport("License.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern Int16 peak_BurnDongle(
        Int16 iType,
        [MarshalAs(UnmanagedType.LPStr)] ref string pszSysID,
        int iProgPort,
        Int16 iSigPort);

Problem:
Sometimes I get an AccessViolationException. Unfortunately this error is not deterministic. After some dongle-writes it occures or not. The best chance to cause this error is if I click the button (execute the code above) fast, e.g. every 2 seconds.

Doese anyone see an obvious mistake? Thanks!

Windows EventLog:

Anwendung: VizLicInitializer.exe
Frameworkversion: v4.0.30319
Beschreibung: Der Prozess wurde aufgrund eines Ausnahmefehlers beendet.
Ausnahmeinformationen: System.AccessViolationException
Stapel:
   bei System.Drawing.SafeNativeMethods+Gdip.GdipCreateBitmapFromStream(IStream, IntPtr ByRef)
   bei System.Drawing.Bitmap..ctor(System.Type, System.String)
   bei System.Windows.Forms.ThreadExceptionDialog..ctor(System.Exception)
   bei System.Windows.Forms.Application+ThreadContext.OnThreadException(System.Exception)
   bei System.Windows.Forms.Application+ThreadContext.PreTranslateMessage(MSG ByRef)
   bei System.Windows.Forms.Application+ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(MSG ByRef)
   bei System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
   bei System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
   bei System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
   bei System.Windows.Forms.Application.Run(System.Windows.Forms.Form)
   bei VizLicInitializer.Program.Main()

EDIT:
Thanks to Luc who has sent me the following solution (still not solving my problem but still a better solution than mine):

        internal static void RegisterWindowCreatedEvent()
        {
            var hHook = SetWindowsHookEx(HookType.WH_SHELL, winDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId());
        }

        private static int ShellHookProcDelegate(int code, IntPtr wParam, IntPtr lParam)
        {
            if (code != HSHELL_WINDOWCREATED)
            {
                return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
            }

            StringBuilder wCaption = new StringBuilder();
            GetWindowCaption(wParam, wCaption, wCaption.Capacity);
            if (wCaption.ToString() == "Information")
            {
                SendMessage(wParam, 0x0010, IntPtr.Zero, IntPtr.Zero);
            }
            return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }


        private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
        private static readonly HookProc winDelegate = ShellHookProcDelegate;
        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, int dwThreadId);

        [DllImport("user32.dll")]
        private static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
        const int HSHELL_WINDOWCREATED = 1;

        [DllImport("user32.dll", EntryPoint = "GetWindowText",CharSet = CharSet.Auto)]
        static extern IntPtr GetWindowCaption(IntPtr hwnd,StringBuilder lpString, int maxCount);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

Following Hans Passant's approach I used Application Verifier to get the following error details.

At the beginning of the stack trace I see some "USER32" and "SendMessage" lines. I use SendMessage to send a closing message to the MessageBox window. But why is it happening? I must say I have no idea what the rest of the stack trace means.

<avrf:logfile xmlns:avrf="Application Verifier">
    <avrf:logSession Version="2" PID="4024" TimeStarted="2016-06-22 : 15:38:26">
        <avrf:logEntry Severity="Error" StopCode="0x13" LayerName="Heaps" Time="2016-06-22 : 15:38:54">
            <avrf:message>First chance access violation for current stack trace.</avrf:message>
            <avrf:parameter1>0 - Invalid address causing the exception.</avrf:parameter1>
            <avrf:parameter2>94e5d8f - Code address executing the invalid access.</avrf:parameter2>
            <avrf:parameter3>2ad984 - Exception record.</avrf:parameter3>
            <avrf:parameter4>2ad9a0 - Context record.</avrf:parameter4>
            <avrf:stackTrace>
                <avrf:trace>vrfcore!VerifierDisableVerifier+73c ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+6c7462f0 ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+6c747982 ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+6c74728a ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!RtlReleasePrivilege+114 ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!RtlGetGroupSecurityDescriptor+30c ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!RtlGetGroupSecurityDescriptor+211 ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!KiUserExceptionDispatcher+f ( @ 0)</avrf:trace>
                <avrf:trace>HLPROG!GetListOfParPorts+f86a ( @ 0)</avrf:trace>
                <avrf:trace>verifier!VerifierGetProviderHelper+ce0d ( @ 0)</avrf:trace>
                <avrf:trace>vrfcore!VerifierTlsSetValue+431 ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+6c746ba5 ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!wcsncmp+58 ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!EtwEventRegister+135 ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!RtlImageNtHeader+2b1 ( @ 0)</avrf:trace>
                <avrf:trace>ntdll!LdrLoadDll+66 ( @ 0)</avrf:trace>
                <avrf:trace>vfbasics!+6c746fd7 ( @ 0)</avrf:trace>
                <avrf:trace>KERNELBASE!GetDiskFreeSpaceExA+2bd0 ( @ 0)</avrf:trace>
                <avrf:trace>clr!StrongNameFreeBuffer+201d1 ( @ 0)</avrf:trace>
                <avrf:trace>clr!StrongNameFreeBuffer+20234 ( @ 0)</avrf:trace>
                <avrf:trace>clr!PreBindAssemblyEx+1747a ( @ 0)</avrf:trace>
                <avrf:trace>clr!PreBindAssemblyEx+17edb ( @ 0)</avrf:trace>
                <avrf:trace>clr!CreateAssemblyNameObject+2f57 ( @ 0)</avrf:trace>
                <avrf:trace>clr!CreateAssemblyNameObject+2ec2 ( @ 0)</avrf:trace>
                <avrf:trace>clr!CreateAssemblyNameObject+2dcb ( @ 0)</avrf:trace>
                <avrf:trace>clr!CreateAssemblyNameObject+2d64 ( @ 0)</avrf:trace>
                <avrf:trace>clr!DllCanUnloadNowInternal+173 ( @ 0)</avrf:trace>
                <avrf:trace>clr!+5ba12a0c ( @ 0)</avrf:trace>
                <avrf:trace>????????+07F73967</avrf:trace>
                <avrf:trace>????????+07F73645</avrf:trace>
                <avrf:trace>????????+07F73579</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cb918a ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cbb71c ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+5827c720 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+582590cc ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+585b8aae ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+585b7841 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cc1d00 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cc6bb1 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cc6b99 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cc6ae0 ( @ 0)</avrf:trace>
                <avrf:trace>????????+0035A093</avrf:trace>
                <avrf:trace>USER32!gapfnScSendMessage+1cf ( @ 0)</avrf:trace>
                <avrf:trace>USER32!gapfnScSendMessage+2cf ( @ 0)</avrf:trace>
                <avrf:trace>USER32!gapfnScSendMessage+908 ( @ 0)</avrf:trace>
                <avrf:trace>USER32!DispatchMessageW+f ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57d1b6bc ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cd5701 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cd5389 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cd5202 ( @ 0)</avrf:trace>
                <avrf:trace>System.Windows.Forms.ni!+57cb15e1 ( @ 0)</avrf:trace>
            </avrf:stackTrace>
        </avrf:logEntry>
    </avrf:logSession>
</avrf:logfile>
Community
  • 1
  • 1
onestarblack
  • 774
  • 7
  • 21
  • 1
    The stack trace is not useful, it shows the built-in unhandled exception reporter failing. All info about the original exception is invisible. But pretty safe to assume that the process is thoroughly corrupted when even reporting the error cannot work anymore. You'll need much bigger weapons to diagnose this, start with UMHD.exe and Application Verifier. – Hans Passant Jun 22 '16 at 09:10

1 Answers1

1

I think your code tries to write to dongle before it closes the popup window.

Consider this: 1. You start the timer and write to dongle. 2. Timer fires, but the popup is not there yet, so nothing happens. 3. The popup shows as you click the button again. 4. Before the timer is fired again (and popup closed), you call WriteDongle.

Try disabling the button before starting the timer and enabling it when you close the popup.

Alternatively, you might try to hook to some window creation event, filter out the one you need and call your close popup code there (never done it myself, so this is just a bit of brain-storming). You'll still need to watch out to disable writing to dongle while the popup is shown. How can I be notified when a new window is created on Win32?

Community
  • 1
  • 1
Luc
  • 1,488
  • 1
  • 13
  • 31
  • At the productive application disable all the buttons ont he form (at my example here this part is missing). But anyways disabling and enabling the button does not solve it. I've tried the solution of your suggested link. But there I get the same error like in my solution. I'm afraid I have to investigate the error as Hans Passant points out in his comment at the original post. – onestarblack Jun 22 '16 at 10:29