AccessViolationException when interacting with RichTextBox - c#

I'm aware there is a bug with .Net 2.0 with LCG which generates AccessViolationException when certain IEnumerable<> and similar objects are used; and that there is a hotfix.
I have a project in .Net 4.0 using WinForms running on Windows 7 x64. I have a very simple form - nothing but a form and a RichTextBox docked to take up the whole client size.
From a background thread, I periodically call an update method on my form that does something similar to the following:
public static void Log(string text)
{
Invoke((MethodInvoker)(() => {
lock(richTextBox) {
richTextBox.Text += text;
}
}));
}
I do actually have both the inside of the invoke and the outside in a try/catch (Yes, I know this is horrible!) and I'm doing some additional things like putting the caret to the end and scrolling to it. I'm also using StringBuilder but this is all beside the point:
Rarely, but inevitably, while debugging, my IDE detects an AccessViolationException somewhere in the code that's updating the text box. It's sometimes in the bit that updates the text, it's sometimes at the bit that makes the selection, and it's sometimes at the bit that scrolls to the caret. If I press F5 to continue, I usually don't see the problem for some time and the application continues as before.
There is nothing fancy happening with this text box. There is no race condition (first of all because I have a lock), but also because there is simply nothing in my code that would try and write to the text box at the same time as something else is trying to.
Any idea why this may be happening? I'm sorry, I don't know what other information I can put here, as I'm not even sure why this is happening in the first place.

Not sure what the rest of your code is doing but I reckon the problem is with lock(richTextBox). If the GUI thread is doing something with the RichTextBox, the lock will not work Or if you are accessing this RichTextBox in some other thread.

Related

Is there a way to see UI updates while debugging?

At the moment I'm trying to debug some code in which I'm cheking for the visibility of items (with the .IsVisible() method for example). The problem is, when I'm jumping from one breakpoint to the next or jumping between lines, the data obviously changes, but the UI of the program doesn't seem to change at all. That makes it a bit difficult for me to tell if things are visible and I have to trust Visual Studio.
Is there a way I can make the UI update while debugging, so I can see the changes over there as well?
You have to force a synchronous re-render of the UI. You could define this extension method somewhere:
public static void SynchronouslyRedraw(this UIElement uiElement) {
uiElement.InvalidateVisual();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => { })).Wait();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => { })).Wait();
}
and call it on your Window after each breakpoint (using the immediate window, a breakpoint action, an additional line of code etc). It should synchronously re-render the Window in question:
this.SynchronouslyRedraw(); // assuming your breakpoint is in your `Window` class for example.
Note that this method works on any UIElement that is in the visual tree of a Window object.
The UI only updates when you give it a chance - that means not occupying the UI thread with other work (such as your event handler).
There isn't a simple way to do this in the debugger in WPF. In Windows Forms, you can just use Application.DoEvents();, but WPF requires you to await Dispatcher.Yield();, so you can't just execute it whenever you want. Both solve the same problem in much the same way - they give the message loop an opportunity to handle all the pending messages, and then go back to where you left off. Both also have the same weakness - they introduce an opportunity for reentrancy, so be careful.

In Idle cannot access RichTextControl or IME will not work

When reading some RichTextControl properties inside Application.Idle some IME won't work.
Given this simple code:
_richTextControl = new RichTextControl();
Application.Idle += delegate(object sender, EventArgs e) {
btnCopy.Enabled = _richTextControl.SelectionLength > 0;
btnPaste.Enabled = _richTextControl.CanPaste();
};
It'll work fine with most IME I tried but, at least, for Chinese (Traditional, Taiwan) with Microsoft Chinese Traditional Array (6.0) and Chinese Traditional DaYi (6.0) it'll prevent IME to work properly (I tried only with Windows 7, in English and Taiwanese). You can start typing but when Idle code is executed (reading SelectionLength property and invoking CanPaste() method, here I wrote them both but each one is enough to reproduce this) it'll stop working (you type but pop-up window is closed immediately and nothing is sent to RichTextControl).
I checked MSDN about EM_GETSELTEXT and EM_CANPASTE but it says nothing about this issue. I also tried to replace SelectionLength property manually sending EM_GETSELTEXT with SendMessage() but it doesn't change this odd behavior (however I checked WordPad - which uses rich editor - and it works properly even if (AFAIK) MFC commands use idle time to update UI status).
Of course I can move some code into SelectionChanged event and update UI both from Idle and from SelectionChanged (and it'll work) or I may update some flags in SelectionChanged and change UI in Idle but this will break existing code and it'll force a big change in UI library (all commands have not knowledge of Idle or SelectionChanged, they simply return true in a CanExecute() method when their target control allows their execution). If nothing else is viable then I may derive a class from RichTextControl to make these methods/properties callable in Idle (using flags updated elsewhere) but (to avoid a big refactoring) I would know if it's a knows issue, a bug related to IME itself or if there is something else I missed.

Winform has Red X exception

I am using a 3rd party control (ComponentOne) and at intermittently I would get this typical red X box such as this typical image shows. At first I thought I have a GDI leak, so after doing some leg works I verified my GDI numbers are fine when drawing controls.
After Googling around I found that this kind of error happens on the OnPaint() event and therefore even if i put a try and catch when calling the control to Render, it wont catch it.
So my next step is to have the following override in my code.
protected override void OnPaint(PaintEventArgs e)
{
try
{
base.OnPaint(e);
}
catch (Exception ex)
{
this.Invalidate(); //attempt to redraw the control
XmSam.Log(ex);
}
}
I think that should do the trick but I can't recreate this problem all the time and so I haven't been able to fully test the above code yet. My question is, if I render my control and it has exception then this code will attempt to redraw the control. Will this stuck in an indefinite loop and freezes my UI? or would you think whatever caused the exception will go away and upon the second control redraw and it should render fine?
I think that should do the trick
No, you made it far worse. Now your OnPaint() method is running over and over again, probably falling over on the same exception repeatedly. You'll see your program burning 100% core as well.
Getting an exception in OnPaint() is not something you can really survive. There's nothing to look at for the user, that's a guaranteed support call. Instead of hiding the problem, use the exception to figure out what actually went wrong and fix the problem. Use Debug + Exceptions, tick the Thrown checkbox for CLR exceptions so the debugger will stop when the exception is raised. Just in case, it is not unlikely that the exception is raised in framework code, also use Tools + Options, Debugging and untick the "Enable Just My Code" option.
When I have seen this in the past it always had to do with a threading issue. Are you updating this control from a different thread than the UI thread?
Its been a while since I've dealt with that but I think the easiest thing to do is use the BackGroundWorker Class to perform background operations and update form controls.
BackGroundWorker Class

Red Cross in DevExpress XtraGrid

Sometimes an XtraGrid gets a red cross instead of data.
( http://www.devexpress.com/Support/Center/p/CB4246.aspx , http://www.devexpress.com/Support/Center/p/A884.aspx and so on )
I have enabled to break on exceptions (Ctrl+D, E, selected all exceptions) - but am not sure if anything else can happen so that the red corss comes.
Is there an easy way if you are in the state "XtraGrid shows red X" that the XtraGrid can get to work again (as far as i seen only closing a form and reopening it helped).
I'm searchign for something like
GridView gridView = this. GridViewXYZ(objectview);
// objectView is refreshed
gridView.RefreshData(); // <- this can thorw an exception that data is not available / UI will get an red cross
// ??? do something to reset the gridView in a workig condition
From my previous GDI experience once you're in this state you're stuck. You've done something that has caused a problem within GDI and it's not managed to throw an exception/recover from it.
The act of closing/re-opening the Form causes the un-managed GDI object for it to be released and re-created, and hence GDI is fully operable again, until the same error condition occurs. If you can't prevent this error, you could have a look at trying to re-create a new GDI object for the form/control but I'm not sure how you'd go about doing so.
Really this is a bug which the DevExpress team should probably solve.
You could try to call BeginInvoke on the Grid.
Action a = () => gridView.RefreshData();
gridView.GridControl.BeginInvoke(a);
Whenever fiddeling with the datasource, you could try wrapping it in a statement like this.
I have expirensed something simular, and you be supprised how often some like this works.
Is there an easy way if you are in the state "XtraGrid shows red X"
that the XtraGrid can get to work again
Yes if you never get into the error state in the first place. Otherwise workaround it by closing/reopening.
The article you link to has info on what causes the "Red Cross of Death" to appear, either a unhandled exception occurs during painting, you handle a control's CustomDraw event and an exception occurs or methods are being called asynchronously.
If you have difficulty in determining the cause of the problem, please try to recreate the issue in a sample project and send it to the DevExpress Support Team.
Having same problem sometimes with Visusa Studio extension CodeRush (also provided by devExpress) when showing references dialog. So it seems to be a internal problem. Hope the next version of devexpress will fix the problem.

AxAcroPDF swallowing keys, how to get it to stop?

The AxAcroPDF swallows all key-related events as soon as it gets focus, including shortcuts, key presses, etc. I added a message filter, and it doesn't get any key-related messages either. It's a COM component, could that be relevant?
Is there any way to catch these before the control starts swallowing them?
Hans is correct, the Acrobat Reader spawns two child AcroRd32 processes which you have no direct access to from within your managed code.
I have experimented with this and you have three viable options:
You can create a global system hook, and then look for and filter out / respond to WM_SETFOCUS messages sent to your child AcroRd32 windows. You can accomplish some of this from within C# by using a wrapper library, such as the one here: http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
You also need to identify the correct processes as there may be more than one instance of your application, or other instances of AcroRd32. This is the most deterministic solution, but because your application will now be filtering messages sent to every single window in existence, I generally don't recommend this approach because then your program could negatively affect system stability.
Find an alternate PDF viewing control. See this answer for a few commercial components: .net PDF Viewer control , or roll your own: http://www.codeproject.com/KB/applications/PDFViewerControl.aspx
Find an acceptable hack. Depending on how robust your application needs to be, code such as the following may be suitable (it was suitable for my case):
DateTime _lastRenav = DateTime.MinValue;
public Form1()
{
InitializeComponent();
listBox1.LostFocus += new EventHandler(listBox1_LostFocus);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
axAcroPDF1.src = "sample.pdf"; //this will cause adobe to take away the focus
_lastRenav = DateTime.Now;
}
void listBox1_LostFocus(object sender, EventArgs e)
{
//restores focus if it were the result of a listbox navigation
if ((DateTime.Now - _lastRenav).TotalSeconds < 1)
listBox1.Focus();
}
I might finally have a ridiculously simple answer. So far in testing this is working.
Having suffered from this problem for quite some time and having built a complex system of each custom control recording which of them last had focus and using a timer to flip focus back (when acropdf grabbed it) I revisited this problem and read a great number of answers (looking for recent solutions). The information gleaned helped me with the idea.
The idea is to disable the (acropdf) control whilst it is loading as in the following example (code reduced for clarity)
AxAcroPDF_this.Enabled = False
AxAcroPDF_this.src = m_src
Then on a timer, after say 1 second.
AxAcroPDF_this.Enabled = True
Basically the idea is to tell Windows not to let users use the acropdf control until allowed, so asking Windows to prevent it from getting focus (because users are not allowed in there).
So far this is holding up, I will edit this if anything changes. If it doesn't work completely for you then maybe the idea points into a useful direction.
It is an out-of-process COM component, that's the problem. Completely in violation of Windows SDK requirements as laid out in SetParent(). Once its window gets the focus, the message loop in the acroread.exe process gets all the messages, your message filter cannot see any messages anymore.
Technically it is fixable by using SetWindowsHookEx() to inject a DLL into the process and monitor messages with WH_GETMESSAGE. But you can't write such a DLL in the C# language.
Major suck, I know. There never seems to be any lack of it with that program.
For some reason Tim's answer, disabling the AxAcroPDF control directly, didn't work in my case. The Leave event on the previously-selected Textbox would never fire, either.
What is working is nesting the AxAcroPDF control inside of a disabled GroupBox. Since the users of my application need to only see the PDF, not interact with it, the GroupBox's Enabled property is set to False in the designer.

Categories

Resources