Memory leak when using WPF WebBrowser control in multiple windows - c#

I am working on a project that makes use of the WPF WebBrowser control (System.Windows.Controls.WebBrowser). The web browser element of the program is one of many activities the user can engage in, and is opened in a separate window. After the user navigates away from the browser, the window is closed, and a new window is created each time the user returns to the browser. We were noticing a significant memory leak / performance downgrade in our program (usage getting up to ~700mb from ~200 initial) upon continually using the browser. After failing to find any points of resource leaks within our own code, I decided to determine if the issue was with our own WebBrowser wrapper control, or the WPF control.
I created a new simple project consisting of only a MainWindow and a WebWindow. A button on the main window launches a browser directed at gmail (the site we noticed the biggest issue with of the few we examined). Upon closing this window, there is no freeing of resources (no reduction in VM size in Task Manager or Process Explorer) and the number of GDI objects the process has handles to does not decrease (the program starts with ~30, opening the browser takes it to ~140 and after closing the browser ~140 are still open). Opening another browser causes more handles, and more resources to be allocated. Furthermore, this problem is not fixed by specifically calling Dispose() on the WebBrowser control. The code is simple, and is as follows:
Main Window:
<Window x:Class="WebBrowserMemory.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Button Click="Button_Click">Gmail</Button>
</StackPanel>
</Grid>
</Window>
Button_Click:
private void Button_Click(object sender, RoutedEventArgs e)
{
var win = new WebWindow();
win.Show();
win.Browser.Navigate("http://www.gmail.com");
}
Web Window:
<Window x:Class="WebBrowserMemory.WebWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WebWindow" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<WebBrowser Grid.Row="0" x:Name="_browser" />
<Button Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10" Padding="10" Click="Button_Click">Close</Button>
</Grid>
</Window>
Relevant Code:
public WebBrowser Browser {
get { return _browser; }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Close();
}
protected override void OnClosed(EventArgs e)
{
_browser.Dispose();
base.OnClosed(e);
}
Has anyone else encountered this issue using the WPF WebBrowser control?
[UPDATE: Updated post to indicate Dispose() call as per itowlson's answer - even calling Dispose() on the web browser control does not free the resources]

Unlike most WPF controls, WebBrowser (because it inherits from HwndHost) is IDisposable and encapsulates unmanaged resources. The WPF Window, unlike the WinForms Form, does not automatically dispose its children (because native WPF controls do not encapsulate unmanaged resources and do not require disposal).
Add an OnClosed override to your window (or handle the Closed event), and call Dispose on the WebBrowser control.

I have not been able to completely solve the leak, however,I have noticed that navigating the browser to "about:blank" prior to disposal definitely helps reduce the amount of memory that hangs around.

We instead used WinForm WebBrowser control, which was created inside FormsHost in WPF, however both work pretty same from the UI point of view, but we have found that WebBrowser of WinForms has better functionality and better performance compared to one given in WPF.
You can manually dispose WebBrowser of WinForm control that will certainly dispose all of its children and free resources accordingly, however with my past experience, WinForm's WebBrowser does not release 100% of its resources after closing, but yes it is far better then WPF.

Related

C# WPF Memory leak when Window has a TabControl with a TabItem (or delayed memory reclaim?)

I experienced what appears to be a very obvious memory leak in an application that would open a settings window containing a TabControl with a number of TabItems. Initially believing that one of the user controls shown must be the culprit I commented out a bunch of things, finally getting out JetBrains dotMemory and making a demo-program.
The issue (I think)
When a Window contains a TabControl with at least one TabItem, when the window is closed, the Window-object still exists. If there are no TabItems in the TabControl, the Window-object is destroyed immediately (as expected).
Retention
According to dotMemory the 'Retention' is from WindowAutomationPeer(._owner), from TabControlAutomationPeer(._parent), from TabItemAutomationPeer(._parent), from ElementProxy(._peer) and then it says "RefCounted handle" at the bottom.
Reproduction
Create a new C# WPF application (Target framework: .NET Framework 4.7.2), named "TabsInWindows"
Add a button to MainWindow:
<Window x:Class="TabsInWindows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<Grid>
<Button Content="Open tab window" Click="Button_Click"/>
</Grid>
</Window>
Create a new Window, "TabsWindow" with a TabControl and a TabItem:
<Window x:Class="TabsInWindows.TabsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="TabsWindow" Height="200" Width="300">
<Grid>
<TabControl x:Name="Subject">
<TabItem></TabItem>
</TabControl>
</Grid>
</Window>
Make the button in MainWindow open a new TabWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//TabsWindow.Open(this);
TabsWindow w = new TabsWindow();
w.Show();
}
}
Start the application. Every time you press the button a new window is created, but the TabItem (?) and therefore the TabWindow remains when the window is closed. (Repeat any number of times)
How to fix?
My issue is that in my actual application all contents of all tabs appears to be retained in memory, causing a significant memory leak.
I have tried, in the demo application, to do a number of things to avoid the hanging objects; Setting content of Grid to null. Clearing the Items in TabControl (Subject). Clearing the Children of the Grid.
None of it has worked.
I cannot work out what the 'AutomationPeer'-objects are or what the ElementProxy is created by and why it won't die.
If anyone can tell me how to get around this issue, or can shed some light on what ElementProxy is and why it is hanging around, it would be most helpful.
While writing this, I did keep dotMemory running with the test app and a while after having done anything last, the objects did appear to have been removed....
Which then raises the question: How long can I expect an object to be visible in memory, with references, before it is removed?
In an actual project
I then tried something similar in an actual project, ensuring that non of our own controls were directly linked to SettingsWindow (I'm not ruling out that I have a memory/reference issue in one of our controls, so any control listed directly in "Key Retention Paths" have been commented out).
I am left with "3 unique branches", one being an 'EffectiveValueEntry[40]' from out own extension of a ListBox, the other two are 'EffectiveValueEntry' ([19] and [22] respectively), both from a TextBlock, from TextBlockAutomationPeer[4], List, ListBoxItemAutomationPeer, ElementProxy.
After about ten minutes of doing nothing, the SettingsWindow was still there, but the "Key Retention Paths" has changed, and the "20 unique branches" are all EffectiveValueEntry ([32] on the first, [42] on the rest), TextBox, TextEditor, but now "F-Reachable Queue" is in the bottom of the list.
After about ten minutes more, the SettingsWindow was finally gone.
I then opened the settings window a few times again, and a minute after closing the last, only the 'TextBox'-references where left and a forced Garbage Collection later (using the button in dotMemory), the object references are gone.
What to believe?
So apparently, if I wait long enough 'magic' will happen - but this is a computer - not a magic-box!
Can anyone enlighten me on why some objects will appear in memory longer, but eventually be removed? How long should I expect such objects to lay about?
I would also like a way to prevent these 'ghost' objects from the TabItems, there should be no reason for them to take up memory if they will eventually be removed anyways...
You see, I discovered this while doing performance testing of some UI components in the SettingsWindow, and repeated tests took longer and longer as more memory was used, so simply waiting for the references to go away is not a very good option.
And if you are unable to help; thank you for taking the time to read my wall of text...
Set the owner of the TabWindow's
private void Button_Click(object sender, RoutedEventArgs e)
{
TabsWindow w = new TabsWindow();
w.Owner = this;
w.Show();
}

Window or class library isn't closing

If i close my application with the X Button, something is still open and i have to close it via the stop button on visual studio.
It only happens when i open a second window which uses a lot of class librarys.
is there any way to determinate which part of the application is still running?
It isn't enough information for finding a fix for me. But I guess that some of your background processes aren't closed or something similar. You have to check your background processes more deeply.
I recommend you to use Parallel Tasks and Parallel Stacks windows. You could read there more about them:
How to: Use the Parallel Watch Window
Using the Parallel Stacks Window
Walkthrough: Debugging a Parallel Application
I think that this could help find a reason for such behaviour.
Go to your app.xaml and after StartupUri="..." add this ShutdownMode="OnMainWindowClose". That should solve your problem.
In your MainWindow.Xaml add Close event
<Window x:Class="ExertERPDesktop.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="Auto" Width="Auto" x:Name="window" TabIndex="0" Foreground="#FFFF00FF" Closed="OnClose">
and in your code behind
private void OnClose(object sender, EventArgs e)
{
Application.Current.Shutdown();
}

How to open Page1.xaml into MainWindow.xaml

I have what should be a very simple desktop application I'm working on but I'm having issues doing a few basic tasks. I'm using Visual Studio 2013.
I have created a project from a blank WPF template. I created a new Page, named Page1.xaml, to go along with the default MainWindow.xaml Window.
In my MainWindow.xaml window I have Grid and inside the grid is an Image.
<Grid MouseDown="Grid_MouseDown_1" Cursor="Hand" >
<Image Name="ImageIntro" Source="images/Stories-intro.jpg" Stretch="None" />
</Grid>
The Grid has a MouseDown event so that I can detect when a user clicks anywhere inside the Grid.
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
}
Pretty basic and that all works and compiles as intended.
The issue I have is that I'm unable to load the Page1.xaml inside my window on MainWindow.xaml. I don't want to open a separate window, I just want the content on Page1.xaml to be displayed inside the visible window of MainWindow.xaml.
I tried using the following but I get an error when I click the on my link: An unhandled exception of type 'System.NullReferenceException' occurred
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
Uri uri = new Uri("Page1.xaml", UriKind.Relative);
NavigationService ns = NavigationService.GetNavigationService(this);
ns.Navigate(uri);
}
This is not a browser application, it's simply a desktop application. The first screen (MainWindow.xaml) should just click thru to display the second screen (Page1.xaml).
I want the Page1.xaml content to take up the entire Window of MainWindow.xaml (sorry, but I can't stress that enough, I don't want a frame or any content from MainWindow.xaml showing when the user is on Page1.xaml).
I'm pretty new to Desktop apps but I have extensive knowledge with .Net C# for web applications. I'm not against changing the flow of what I have if there's a better way to accomplish this. For example, perhaps I shouldn't be using a Window to Page navigation and should instead use a Window to Window or something else.
I would imagine this would be a relatively simple task, but I haven't found anything that works yet so hopefully someone on here can explain it.
create a frame in Main Window
then in your event hander
Page1 mypage=new Page1();//object of the page 1
frame.Navigate(mypage);//pass it to frame navigate method
Read this MSDN link, the requirements for that to work say that this (in your code) must be a Frame. I don't see any XAML code here, but I'm guessing this is a Window. You need a frame to host the navigation, so your MainWindow should probably just the frame, and the contents of your current window should be "Page0".
Thanks Everyone who helped, here is the solution I used (I wanted to make sure a code example was here for anyone who has this same issue in the future).
I added a Frame to my MainWindow.xaml page:
<Grid MouseDown="Grid_MouseDown_1" Cursor="Hand" >
<Image Name="ImageIntro" Source="images/Stories-intro.jpg" Stretch="None" />
<Frame Name="Frame1" Content="" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
Then I added the following to my event:
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
Page1 mypage = new Page1();
Frame1.Navigate(mypage);
}
This allowed me to click on my link and the new page, Page1.xaml appeared. There was also a navigation bar that appeared at the top and the content didn't completely take up the MainWindow.xaml window but I think I can fiddle around with the settings and get it to where I want it.
Much thanks to Filippo B, Nauman Ahmad, and CodingGorilla for the assist.

ContentControl is not visible when application starts via UI Automation test, but its visible when application starts by user

We are using the prism and WPF to build application. Recently we started using UI Automation (UIA) to test our app. But some strange behavior occurred when we run UIA test. Here's simplified shell:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0" Grid.Column="0"
Name="loadingProgressText"
VerticalAlignment="Center" HorizontalAlignment="Center"
Text="Loading, please wait..."/>
<Border
Grid.Row="0"
x:Name="MainViewArea">
<Grid>
...
</Grid>
</Border>
<!-- Popup -->
<ContentControl
x:Name="PopupContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="PopupRegion"
Focusable="False">
</ContentControl>
<!-- ErrorPopup -->
<ContentControl
x:Name="ErrorContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="ErrorRegion"
Focusable="False">
</ContentControl>
</Grid>
In our app, we use layers (Popup and ErrorPopup) to hide MainViewArea, to deny access to the controls. To show Popup, we use next method:
//In constructor of current ViewModel we store _popupRegion instance to the local variable:
_popupRegion = _regionManager.Regions["PopupRegion"];
//---
private readonly Stack<UserControl> _popups = new Stack<UserControl>();
public void ShowPopup(UserControl popup)
{
_popups.Push(popup);
_popupRegion.Add(PopupView);
_popupRegion.Activate(PopupView);
}
public UserControl PopupView
{
get
{
if (_popups.Any())
return _popups.Peek();
return null;
}
}
Similar to this, we show ErrorPopup over all elements of our application:
// In constructor we store _errorRegion:
_errorRegion = _regionManager.Regions["ErrorRegion"]
// ---
private UserControl _error_popup;
public void ShowError(UserControl popup)
{
if (_error_popup == null)
{
_error_popup = popup;
_errorRegion.Add(_error_popup);
_errorRegion.Activate(_error_popup);
}
}
Mistics...
When we run it as users do it (double click on app icon), we can see both custom controls (using AutomationElement.FindFirst method, or through Visual UI Automation Verify). But when we start it using UI Automation test - ErrorPopup disapears from the tree of the controls. We trying to start the application like this:
System.Diagnostics.Process.Start(pathToExeFile);
I think that we missed something. But what?
Edit #1
As #chrismead said, we tried to run our app with UseShellExecute flag set to true, but this does not help. But if we start app from cmd line, and manually click the button, Popup and ErrorPopup are visible in automation controls tree.
Thread appThread = new Thread(delegate()
{
_userAppProcess = new Process();
_userAppProcess.StartInfo.FileName = pathToExeFile;
_userAppProcess.StartInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
_userAppProcess.StartInfo.UseShellExecute = true;
_userAppProcess.Start();
});
appThread.SetApartmentState(ApartmentState.STA);
appThread.Start();
One of our suggestion is when we use method FindAll or FindFirst to search the button to click, window somehow cached its UI Automation state, and does not update it.
Edit #2
We have find, that extension method of prism library IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) have some strange behavior. If we stopped use it, this solve our problem particulary. Now we able to see ErrorView and any kind of view in PopupContentControl, and application updates UIA elements tree structure. But this is not an answer - "Just stop use this feature"!
In MainViewArea we have a ContentControl, which updates it content depending on user actions, and we are able to see only the first loaded UserControl to that ContentControl.Content property. This is performed like this:
IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);
And if we change the view, no updates will performed in UI Automation tree - the first loaded view will be in it instead. But visually we observe another View, and WPFInspector shows it properly (its show not a UI Automation tree), but Inspect.exe - not.
Also our suggestion that window use some kind of caching is wrong - caching in UI Automation client we have to turn on explicitly, but we don't do it.
I'm sorry that I've missed some detail, that was the key to the answer. I think that it was not important thing. Anyway.
We used NavBar from DevExpress controls library for WPF. What turns out, is when NavBar is present, dynamically created views are not appears on the UI Automation tree. When remove it from the window, there was an ability to see all dynamically loaded views. What does the NavBar - still mistic for me.
Here bright example to see what happened, if NavBar is present or absent on the Window (DevExpress is required).
MainWindow.xaml:
<Window xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
>
<Grid Name="ContentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<!--Comment NavBar to see dynamic control in UI Automation tree-->
<dxn:NavBarControl Name="asdasd">
<dxn:NavBarControl.Groups>
<dxn:NavBarGroup Header="asdasdasdasd" />
</dxn:NavBarControl.Groups>
</dxn:NavBarControl>
<TextBox Grid.Column="1" Name="Statictb" Text="static is visible in ui automation tree" />
<Button Grid.Row="1" Content="Create controls" Height="25" Click="Button_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBox tb = new TextBox();
Grid.SetRow(tb, 1);
Grid.SetColumn(tb, 1);
tb.Text = "dynamic is not visible, if NavBar here...";
ContentGrid.Children.Add(tb);
}
}
Edit
According to the DevExpress answer on their support site:
After a peer is created, listening of automation events may cause performance issues. We have decided to clear invocation lists of automation events to resolve it. In your specific situation, you need to disabling clearing. To do it, please set the static DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled property to False in the Window constructor.
This solve the problem.
My guess is that the ContentControl's automation peer should update its children with AutomationPeer.ResetChildrenCache() after the view has been changed.
AutomationPeer.InvalidatePeer() should have the same effect (in addition to other side effects) and it is supposed to be called automatically in response to the LayoutUpdated event. You might want to check that the LayoutUpdated event is raised when the view changes.
stukselbax, try to find a sequence of keystrokes (TABs, and an ENTER most likely) to click the button that enables you to see the items. it is pretty easy to send keystrokes and i can add more in here about that if that works for you. you can always establish a tab order in your application that makes the most sense for users.
------ Update on 6/20/12 --------
Have you tried double clicking a shortcut to your app on the desktop using PInvoke to see if you can see the controls when it is opened that way? Here is a link to an example here on stackoverflow:
Directing mouse events [DllImport("user32.dll")] click, double click
Another idea: some of the controls on the app I am currently automating don't show up in the tree until a mouse click occurs on them. To accomplish this without using any hardcoded coordinates, I find something in the tree which is just (above/below/etc) the place where I need to click to get the control to appear. I then get the mouse coordinates for that item and put the mouse at a small offset from there and click. Then I can find my controls in the tree. If the app is resized, moved around, etc. this will still work since the small offset is still valid.

WPF WindowsFormsHost Winform user control shows but does not function

I am trying to migrate an existing Winforms project into WPF. However: there are some user controls I need to leave as WinForm controls.
I have added a WinForms UserControl into a WPF Window. It consists of a RichTextBox and some buttons and labels. This is subclassed into various further user controls.
When I embed the UserControl into a WPF window it renders - but none of the buttons appear to do anything. When underlying processes update e.g. the RichTextBox it does not display the content. Yet when I inspect the textbox in debug I can see the content (though I have to click on 'base' to see this.)
[ One difference I have spotted - though it may not be relevant - is that when this control is on a WPF and non-working Visual Studio shows the object as 'sealed' but when in the original Winforms project when it is fully working it does not show as sealed. ]
I have added code to change the text in the labels - and they also firmly refuse to update: yet again I can see the text if I examine the label in debug mode.
This stack overflow question may address the same issue:
WindowsFormsHost Winform pdfviewer control problem
but the answer didn't make a lot of sense to me:
It mentioned replacing
new Window { Content = CreateContent(), Title = title }.Show();
But this is not a piece of code I recognise: I am using a xaml file with code behind and it's called up using
System.Windows.Application app = new System.Windows.Application();
app.Run(new FormWPFApp());
(where FormWPFApp is my name for the WPF window)
Here is the xaml header:-
<Window x:Class="ZedApp.FormWPFApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Printers="clr-namespace:ZedApp.UserControls.Printers"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="Conversion version" Height="661" Width="1559" Loaded="Window_Loaded">
Here is the xaml I use for the two UserControls (they both inherit from the same base class) :-
<WindowsFormsHost Height="430" HorizontalAlignment="Left" Margin="192,32,0,0" Name="windowsFormsHostTicketPrinter" VerticalAlignment="Top" Width="324" Grid.Row="1" Grid.Column="1">
<Printers:TicketPrinter x:Name="ticketPrinter">
</Printers:TicketPrinter>
</WindowsFormsHost>
<WindowsFormsHost Height="430" HorizontalAlignment="Left" Margin="522,32,0,0" Name="windowsFormsHostJournalPrinter" VerticalAlignment="Top" Width="324" Grid.Row="1" Grid.Column="1">
<Printers:JournalPrinter x:Name="journalPrinter">
</Printers:JournalPrinter>
</WindowsFormsHost>
[Another thing I have noticed is a method that clears the Rich Text Box on one of the windows starts kicking out errors of the following type if run under WindowsFormsHost in WPF -
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
private void ClearRichTextBox(RichTextBox rtbToClear)
{
if (rtbToClear.IsHandleCreated)
{
if (rtbToClear.InvokeRequired)
{
this.Invoke(new Action<RichTextBox>(ClearRichTextBox), new object[] {rtbToClear});
return;
}
rtbToClear.Clear();
}
}
]
What is the likely cause of this behaviour and what do I need to do to get the elements within the User Control working?
Proper input interop with WinForms requires some cooperation between the host and the WPF input system. The topic Message Loops Between Win32 and WPF in the SDK explains this well. In your setup, the easiest way to make this happen is to use code like this:
Window w = new Window1();
System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop(w);
w.Show();
ElementHost.EnableModelessKeyboardInterop() essentially registers an input hook with the WinForms Application object (which normally runs the message loop) and calls ComponentDispatcher.RaiseThreadMessage().

Categories

Resources