|
Jacob Dixon wrote: Well even if I called a abort on a thread will it abort during the call to WMI or wait for it to finish?
That's why I said don't worry about aborting it. The behavior of WMI is undefined if the caller is killed off before the WMI call returns. At least, i couldn't find definitive documentation on what the behavior is anyway.
Jacob Dixon wrote: I'm a little new to using threads like this, but creating a new thread (not as background) will run even if the application is closed correct?
If the thread is not tagged as background, the application will not close. Background threads do not prevent the app from closing.
|
|
|
|
|
I agree with Dave. You should set up a class to do the work for you, and when it is complete, it should fire an event that returns the values it found.
As an example, I have a program that has to extract the column information from an OLAP data cube. This process can take a long time, but I need the information to fill out the different parts of the form. But, sometimes, the user has finished filling out the form before the entire cube was loaded, or they decide to quit. So, on the form close, the first thing I do is to hide the form. As far as the user is concerned, the form is hidden.
Then, I send the call to the class to tell it to abort. The class looks for that abort message periodically as its working, and if it gets it, it will quit loading. Then, in the Form_Close method, I hook the BackgroundWorker.RunWorkerCompleted Event. When that event fires, I know that the process has completed, and I can dispose of the thread and form safely.
In your case, you can't tell it to prematurely stop, but you can let it finish loading in the background, but still hide the form from the user so that they think it is closed.
|
|
|
|
|
Well see I can't abort WMI.
ALL I can do is basically tell my code NOT to write the data to form if it sees the Handle isn't there. Now on the events do you mean something like this:
MAIN THREAD:
public delegate void UserRetrievalDelegate();
public event UserRetrievalDelegate UserRetrievalFinished;
private void frmMain_Load(object sender, EventArgs e)
{
UserRetrievalFinished += new UserRetrievalDelegate(frmMain_UserRetrievalFinished);
}
void frmMain_UserRetrievalFinished()
{
PopulateUsers();
}
USER RETRIEVAL THREAD (Not Main thread):
private void FillUsers()
{
users = new List<User>();
LDAP ldap = Login();
if (ldap != null)
{
PrincipalSearchResult<Principal> usrs = ldap.AllADUsers();
foreach (Principal principal in usrs)
{
User temp = ldap.GetUserInfo(principal.Name, "name");
users.Add(temp);
temp = null;
}
UserRetrievalFinished();
}
}
So the thread fires the event which calls my populateusers method from the main thread?
|
|
|
|
|
Nope.
The way you have it, PopulateUsers is not "from the main thread". It will be executed by the caller, on the caller's thread, as always. You need a delegate and a Control.Invoke to force the method to be called on the main thread; just moving some code from one source location to another cannot solve this.
You may want to read this[^]; you do not really need Control.InvokeRequired here, as you are certain the caller is some other thread.
|
|
|
|
|
Uhm. I see what you are saying also.... but how do I do that if the main thread has been disposed of? Basically in my spawned thread I need to double check and make sure the main thread is not disposed of.. if it is then basically don't try to write anything to it (because you can't), and if it is there then write the data to it
|
|
|
|
|
You seem to be very confused; a .NET program consists of two things: objects and threads;
objects contain code and data however they don't do a thing (they are the cookbook and the ingredients); threads execute code and operate on data (like chefs reading cookbooks and using ingredients), so it is threads that give live to objects (make soup).
If it is your Form that is to display the results of a long-wielding action (which you therefore have relegated to another thread), then you need Invoke() to pass the action back to the main thread (the thread is still alive, all that could have happened is that your Form got closed, disposed, and maybe collected); if your Form no longer exists, then you must prevent the Invoke from happening, so use a boolean flag, or set the delegate to null, or whatever other way you choose to tell the helper thread the results are no longer wanted.
|
|
|
|
|
Oh I understand I must invoke. I also read the article you have sent. So I know I must invoke controls that want to display data from another thread. What I didn't know was how to basically tell the thread NOT to invoke if the main thread wasn't here anymore.
if your Form no longer exists, then you must prevent the Invoke from happening
That is what I'm trying to figure out.. how to tell the form doesn't exist anymore and just not write anything. I just looked and was wondering if this.IsDisposed was a way to make it work
|
|
|
|
|
simplest is using the delegate itself: have the form set up the delegate, and when it decides to go away (you might use its finalizer, I would prefer a real event though, maybe FormClosing), clear the delegate; in the relegated code, check the delegate isn't null.
|
|
|
|
|
What is the harm of this:
private void GetServices()
{
WMI_Commands wmi = new WMI_Commands(Username, Password,
System.Management.ImpersonationLevel.Impersonate, System.Management.AuthenticationLevel.Default);
List<Services> services = wmi.GetServices(NetBios);
if (!this.IsDisposed)
{
foreach (Services s in services)
{
AddServices(s);
}
SetPBVisible("pbservices", false);
}
services = null;
}
I'm testing it right now... (process still running)
Basically I check and make sure that the form isn't disposed.. if it IS then I basically do nothing.. no need to write anything out, but if IsDisposed comes back false then I write it out.
|
|
|
|
|
With the original code you showed, I would normally put it into its own class, but if you didn't something like this:
private delegate void ApplicationsLoadedDel(List<Product> apps);
private event ApplicationsLoadedDel ApplicationsLoaded;
private void frmMain_Load(object sender, EventArgs e)
{
appThread = new Thread(new ThreadStart(GetApplications));
appThread.IsBackground = true;
this.ApplicationsLoaded += AddApplications;
appThread.Start();
}
private void LoadApplications()
{
WMI_Commands wmi = new WMI_Commands(Username, Password,
System.Management.ImpersonationLevel.Impersonate,
System.Management.AuthenticationLevel.Default);
List<Product> products = wmi.InstalledApplications(NetBios);
ApplicationsLoaded(products);
}
Delegate void AddApplicationsDel(List<Product> products)
private void AddApplications(List<Product> products)
{
if (this.InvokeRequired)
this.Invoke(new AddApplciationsDel(Address of AddApplications),
new Object() {products});
foreach (Product p in products)
{
AddApplication(p);
}
products = null;
SetPBVisible("pbapplications", false);
}
|
|
|
|
|
Uhm...
I see what you are doing.. but how does that solve the problem with a user closing the thread before its finished? Because that is somewhat like I thought I had it but the problem is it would still try to write to the form even when it was gone.
Calling ApplicationLoaded from the thread doesn't that not make it on the main thread? Which would end up with the thread still trying to write to a form that isn't there?
|
|
|
|
|
You could set up your Form_close like this:
public void form_close(Object sender, FormClosingEventArgs e)
{
this.Hide();
this.ApplicationsLoaded -= AddApplications;
this.ApplicationsLoaded =+ CloseFormOnCompletion;
}
public void CloseFormOnCompletion(List<Product> products)
{
this.Close();
}
So, the thread will continue to run, but nothing will be written to the form because you've changed what happens when the event is fired off.
|
|
|
|
|
Ah that is a pretty good idea to.. but I think just a simple MyDelegate != null would be easier?
|
|
|
|
|
Solution from all of your guys help:
I have tested this a couple times and it has yet to throw any errors on closing the form early. Still have plenty more testing to do though.
I create my event for when the applications have been loaded and start my threads
private delegate void ApplicationsLoadedDelegate(List<Product> products);
private event ApplicationsLoadedDelegate ApplicationsLoaded;
private void frmCompInfo_Load(object sender, EventArgs e)
{
ApplicationsLoaded += new ApplicationsLoadedDelegate(frmCompInfo_ApplicationsLoaded);
ServicesLoaded += new ServicesLoadedDelegate(frmCompInfo_ServicesLoaded);
InfoLoaded += new InfoLoadedDelegate(frmCompInfo_InfoLoaded);
appThread = new Thread(new ThreadStart(GetApplications));
appThread.Start();
driveThread = new Thread(new ThreadStart(GetInfo));
driveThread.Start();
serviceThread = new Thread(new ThreadStart(GetServices));
serviceThread.Start();
}
This is what I call which makes the call to the WMI DLL file I created for querying the data of a remote computer. It will return a List of my custom structure <product>. Before I do anything else with this I check and make sure that my ApplicationsLoaded is not null. If it is null that means the form has been closed. I do the invoke required blah blah blah because of another thread. I know you suggested not doing it, but I might call this from the main thread also (for the future). Plus the article you sent me the author was really fond of sticking to this technique.
private void GetApplications()
{
WMI_Commands wmi = new WMI_Commands(Username, Password,
System.Management.ImpersonationLevel.Impersonate, System.Management.AuthenticationLevel.Default);
List<Product> products = wmi.InstalledApplications(NetBios);
if (ApplicationsLoaded != null)
ApplicationsLoaded(products);
products = null;
}
private void frmCompInfo_ApplicationsLoaded(List<Product> products)
{
if (lstApplications.InvokeRequired)
lstApplications.Invoke(new ApplicationsLoadedDelegate(ApplicationsLoaded), new object[] { products });
else
{
lstApplications.BeginUpdate();
foreach (Product p in products)
{
ListViewItem item = new ListViewItem();
item.Text = p.Name;
item.SubItems.Add(p.Version);
item.SubItems.Add(p.Vendor);
item.SubItems.Add(p.InstallLocation);
lstApplications.Items.Add(item);
}
lstApplications.EndUpdate();
SetPBVisible(pbApplications, false);
}
}
On form closing I unload all of the events.
private void frmCompInfo_FormClosing(object sender, FormClosingEventArgs e)
{
ApplicationsLoaded -= frmCompInfo_ApplicationsLoaded;
ServicesLoaded -= frmCompInfo_ServicesLoaded;
InfoLoaded -= frmCompInfo_InfoLoaded;
}
I've taken a couple of your suggestions and kind of compiled them together I think. Like I said I have tested this only a couple times by loading the form and watching it start the thread and closing the form before the thread was completed. I never saw any kind of error messages at all. (which is what I wanted)
|
|
|
|
|
You posted whilst I was composing the message below! Hope it helps anyway
DaveIf this helped, please vote & accept answer!
Binging is like googling, it just feels dirtier. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
|
|
|
|
|
Reading through the messages above, you don't seem to have grasped the 'unsubscribe from event' way of dealing with this.
If you make sure that your thread updates the UI only via an event then by unsubscribing to that event, the UI update code will not get called! All the InvokeRequired stuff should be in the event consumer's handler method.
It's not clear whether you wish to let the thread complete naturally after the application closes or terminate it immediately. By setting IsBackground to true , it will terminate. Not setting (or setting to false ) will allow it to continue.
Here's some demo code which may help explain!
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Forms;
namespace ThreadingDemo
{
public partial class FormMain : Form
{
MyThreadedClass myThreadedClass;
public FormMain()
{
InitializeComponent();
myThreadedClass = new MyThreadedClass();
myThreadedClass.GotDataCompleted += myThreadedClass_GotDataCompleted;
FormClosing += FormMain_FormClosing;
Shown += new EventHandler(FormMain_Shown);
}
void FormMain_Shown(object sender, EventArgs e)
{
}
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
myThreadedClass.GotDataCompleted -= myThreadedClass_GotDataCompleted;
}
private void myThreadedClass_GotDataCompleted(object sender, GotDataCompletedEventArgs e)
{
if (InvokeRequired)
Invoke(new MethodInvoker(delegate { myThreadedClass_GotDataCompleted(sender, e); }));
else
{
foreach (string item in e.Data)
Text = item;
}
}
private void RunThread()
{
myThreadedClass.Begin();
}
private void RunThreadAsBackground()
{
myThreadedClass.SetAsBackground();
myThreadedClass.Begin();
}
}
public class MyThreadedClass
{
public event EventHandler<GotDataCompletedEventArgs> GotDataCompleted;
private Thread thread;
public MyThreadedClass()
{
thread = new Thread(new ThreadStart(Start));
}
public void Begin()
{
thread.Start();
}
protected virtual void OnGotDataCompleted(GotDataCompletedEventArgs e)
{
EventHandler<GotDataCompletedEventArgs> eh = GotDataCompleted;
if (eh != null)
eh(this, e);
}
private void Start()
{
GotDataCompletedEventArgs e = new GotDataCompletedEventArgs();
e.AddData("Data 1");
Thread.Sleep(10000);
e.AddData("Data 2");
OnGotDataCompleted(e);
}
public void SetAsBackground()
{
thread.IsBackground = true;
}
}
public class GotDataCompletedEventArgs : EventArgs
{
private List<string> data;
public GotDataCompletedEventArgs()
{
data = new List<string>();
}
public ReadOnlyCollection<string> Data
{
get { return data.AsReadOnly(); }
}
internal void AddData(string item)
{
data.Add(item);
}
}
}
DaveIf this helped, please vote & accept answer!
Binging is like googling, it just feels dirtier. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
|
|
|
|
|
The line in my form is flickering.
The form is DoubleBuffered which made me kind of lost .
Help?
using System;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Design;
namespace Threading
{
public partial class Form1 : Form
{
Pen pen = new Pen(Color.Black);
Thread t;
public Form1()
{
InitializeComponent();
pen.Width = 3;
pen.StartCap = System.Drawing.Drawing2D.LineCap.SquareAnchor;
pen.EndCap = System.Drawing.Drawing2D.LineCap.RoundAnchor;
t = new Thread(new ParameterizedThreadStart(Commando));
t.Start(true);
}
public void Commando(object obj)
{
while (true)
{
Graphics x = this.CreateGraphics();
x.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
x.DrawLine(pen, new Point(0, 0),
new Point(
MousePosition.X - this.Location.X, MousePosition.Y - this.Location.Y));
this.Refresh();
}
}
}
}
Any help is appreciated.
|
|
|
|
|
nbgangsta wrote: this.Refresh();
This will not work, you are trying to refresh the form from a different thread. It will throw a cross thread reference error.
nbgangsta wrote:
t = new Thread(new ParameterizedThreadStart(Commando)); <br />
t.Start(true);
Not sure what you are trying to do. But I would override the form's OnPaintMethod and use the MouseMove event to draw the line rather than using a different thread to draw it.
Tarakeshwar Reddy
There are two kinds of people, those who do the work and those who take the credit. Try to be in the first group; there is less competition there. - Indira Gandhi
|
|
|
|
|
Flickering is a sign that it is not doublebuffered or you are drawing on the wrong surface or flipping too soon.
Double buffering is:
Create a primary surface with one backbuffer (essentially 2 display surfaces)
Always draw on the backbuffer (it is not visible) and when all drawing is complete swap the 2 surfaces.
the swapping makes the backbuffer the primary and the primary becomes the backbuffer
this is what prevents flickering, the instantanious swap of the source display surface.
I haven't used the above drawing methods only DirectX and some GDI so i could be totally wrong about your code.
It appears you are creating/initializing your drawing surfaces (this.CreateGraphics()) repeatedly....this should only be done once.
- Where does it create your drawing surface Doublebuffered?
- How does x.DrawLine know which surface of X to draw on?
I also see "new" several times within you while(true) loop but no "delete" unless their is some sort of garbage collection for memory eventually you'll run out.
Another issue you could have is since the drawing is in it's own thread....when the main form/window needs to refresh, it needs to know what to draw (the primary surface) or let the drawing thread know it needs to refresh the display area that it is handling.
Hope this helped.
|
|
|
|
|
I can't say for sure what's causing it, but I was curious about something in your code:
public void Commando(object obj)
{
while (true)
{
Graphics x = this.CreateGraphics();
x.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
x.DrawLine(pen, new Point(0, 0),
new Point(
MousePosition.X - this.Location.X, MousePosition.Y - this.Location.Y));
this.Refresh();
}
}
you set it up as a parameterized void , and you passed it true, but then you never actually check the object. obj does absolutely nothing in this example.
I don't think you're going to be able to get away from the flicker the way you're doing it.
Why, can't you just put the code into the MouseMove? It will flicker a tiny bit while you're moving, but when the mouse is stopped, no flicker.
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
this.Refresh();
Graphics x = this.CreateGraphics();
x.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
x.DrawLine(pen, new Point(0, 0),
new Point(
MousePosition.X - this.Location.X, MousePosition.Y - this.Location.Y));
}
Oh, and I modified your code to be more thread friendly:
public void Commando(object obj)
{
while (true)
{
Graphics x = this.CreateGraphics();
x.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
x.DrawLine(pen, new Point(0, 0),
new Point(
MousePosition.X - this.Location.X, MousePosition.Y - this.Location.Y));
if (this.InvokeRequired)
{
RefreshMe = RefreshForm;
this.Invoke(RefreshMe);
}
}
}
private delegate void RefreshFormDel();
private RefreshFormDel RefreshMe;
private void RefreshForm()
{
this.Refresh();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (t != null)
if (t.IsAlive)
t.Abort();
}
|
|
|
|
|
Am not sure how can I describe it or what does Microsoft call it but listen...
When you start Windows Vista and you get the login screen it will show the user picture and the password textbox with the work password in light color so when you click inside it will disappear then whenyou clear the text it will show again..
first, what do you call it please.
then how can I do it for both user id and password textbox?
|
|
|
|
|
It's easy enough to crete your own custom TextBox control that does this. There several different ways of doing it, but they all start with creating a seperate class that inherits from TextBox and probably provides some custom painting code and a couple of extra properties to show what you want light-shaded if the Text property is an empty string AND the control does not have the focus.
|
|
|
|
|
but does this thing has a name so I can at least get guide from internet?
|
|
|
|
|
|
|