Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Feb 18th, 2006, 6:05 AM   #1
Pedja
Programmer
 
Join Date: Nov 2005
Location: Belgrade, Serbia & Montenegro
Posts: 31
Rep Power: 0 Pedja is on a distinguished road
Send a message via ICQ to Pedja Send a message via MSN to Pedja Send a message via Yahoo to Pedja
Unhappy Threading, listViews, application breaking... help?

Hi. I have a problem, and don't have a clue how to solve it.
Application contains 1 form, with 3 ListView controls in it. First of them lists known file types with their current icons, the second one lists programs that are recommended for opening or editing files with extension you choose from the first ListView (displays icons too), while the third one displays all icons from the default program that opens files with the selected extension.
When I use the application by mouse clicking from time to time it works fine. But if I start clicking more often, or start to use keyboard, for example when I just hold the down button to go through first ListView, the application goes crazy. It starts drawing what it shouldn't draw, and doesn't draw what it should. Eventually it crashes, reporting error, allways in different place.
Offcourse, I fill second and third ListView in another thread. In the method listView1_SelectedIndexChanged i've tried to wait for the thread to finish, i've tried mutex synchronization... Nothing helps. I think that simple synchronization doesn't help when you work with forms.
Can someone help me? Does anyone know what is, and how to use Control.Invoke(delegate) method, and does it have anything to do with this?
Pedja is offline   Reply With Quote
Old Feb 18th, 2006, 4:55 PM   #2
The Dark
Expert Programmer
 
Join Date: Jun 2005
Posts: 850
Rep Power: 4 The Dark is on a distinguished road
Do you have only one other thread, or do you start a new thread for each SelectIndexChanged event?

One option would be to have only one second thread and have it only update on the last change, rather than all the ones in between. Have a variable that lists the currently selected list item in the first list. When you get the listView1_SelectedIndexChanged message, just update the variable and tell the second thread that the variable has changed (use a semaphor, or something similar). The second thread only looks at this variable after it has finished processing the last change, so that it completes one update before starting the next.

If this is not very understandable, post some code so I can look at it.
The Dark is offline   Reply With Quote
Old Feb 21st, 2006, 6:25 AM   #3
Pedja
Programmer
 
Join Date: Nov 2005
Location: Belgrade, Serbia & Montenegro
Posts: 31
Rep Power: 0 Pedja is on a distinguished road
Send a message via ICQ to Pedja Send a message via MSN to Pedja Send a message via Yahoo to Pedja
Thanks for help. But i'm not sure that would help. I've tried a lot of stuff.

Let's say that you have a ListView, and every SelectedIndexChanged event is causing a slow operation to be executed. For example some hard drive operation like extracting icons from some exe or dll file, and drawing those icons somewhere else. You want to move fast through ListView, and if previous operation has not finished, you want it to be terminated immediately, and the new one to start.

How would you do this?
Pedja is offline   Reply With Quote
Old Feb 21st, 2006, 9:54 AM   #4
The Dark
Expert Programmer
 
Join Date: Jun 2005
Posts: 850
Rep Power: 4 The Dark is on a distinguished road
In form1.txt below is an example program (just save it as form1.cs) which shows what I mean.
It is just a simple app with 2 listboxes. On startup it load all the directories in your C:\ into listBox1. When listbox1 selection changes it calls a worker thread to load the files in the selected directory into listBox2.

Here is the main part of it (the the selection change event handler just calls setDirectoryToLoad
		void setDirectoryToLoad(string dirName)
		{
			m_workerMutex.WaitOne();
			if (m_workerThread == null)
			{
				m_workerThread = new Thread( new ThreadStart(getFilesThread) );
				m_workerThread.Start();
			}
			m_directoryToLoad = dirName;
			m_workerMutex.ReleaseMutex();
			m_workerStarter.Set();
		}

		void getFilesThread()
		{
			string lastDirectoryDone = "";
			while (true)
			{
				// Wait for a start command
				m_workerStarter.WaitOne();
				// Lock the mutex for the shared string
				m_workerMutex.WaitOne();
				string dirName = m_directoryToLoad;
				m_workerMutex.ReleaseMutex();
				if (dirName != lastDirectoryDone)
				{
					lastDirectoryDone = dirName;
					listBox2.Items.Clear();
					try
					{
						string[] dirs = Directory.GetFiles(listBox1.GetItemText(listBox1.SelectedItem));
						foreach (string dir in dirs) 
						{
							Thread.Sleep(100);
							listBox2.Items.Add(dir);

							// Check to see if the dirname has been changed
							m_workerMutex.WaitOne();
							dirName = m_directoryToLoad;
							m_workerMutex.ReleaseMutex();
							if (dirName != lastDirectoryDone)
							{
								// Ensure that the next directory is done (in case it gets changed back before we loop around)
								lastDirectoryDone = "";
								break;
							}
						}
					}
					catch (UnauthorizedAccessException)
					{
					}
				}

			}
		}

setDirectoryToLoad starts up the worker thread if it hasn't already been started. Then it sets up the desired directory into a member variable and uses an event to tell the worker thread to go. A mutex is used to protect access to the directory name member variable.

The getFilesThread function loops around waiting for the event to be triggered. When it gets the event, it grabs the directory name from the member variable (again, using the mutex to prevent concurrent access). If the directory name is different than last time, it loads it into Listbox2 with a delay between each load to simulate doing lots of work. It also does a check after each entry is loaded into listbox2 to see if there is a different directory requested.
Attached Files
File Type: txt Form1.txt (4.6 KB, 19 views)
The Dark is offline   Reply With Quote
Old Feb 21st, 2006, 11:26 AM   #5
MBirchmeier
Hobbyist Programmer
 
Join Date: Oct 2005
Posts: 211
Rep Power: 3 MBirchmeier is on a distinguished road
I'd say you're pretty close dark, but you still have the chance of thread unsafety. The fact that the problem presents itself when the arrow keys are used instead of the mouse leads me to think the problem doesn't lie in a conflict between the OP's code with itself, but the OP's code and the control code.

.NET controls are not inheriently threadsafe, and to get around that you need to call .Invoke() on the control to ensure the specified control code gets executed in the proper thread.

so anyplace inside the mutex that calls a function on listBox2 (or a similar control) should call the .Invoke method instead.

Ex:

listBox2.Items.Add(dir);

should instead call

listBox2.Invoke(myAdd, dir);

which invokes the function myAdd in the proper thread and myAdd should then add dir to the listBox items accordingly.

(If you need to do this in compact framework calling .Invoke with parameters is not supportedhttp://www.iter.dk/ has a download for PocketGPS which has a class called ControlInvoker which affectively gets around this restriction).

-MBirchmeier
MBirchmeier is offline   Reply With Quote
Old Feb 21st, 2006, 5:27 PM   #6
The Dark
Expert Programmer
 
Join Date: Jun 2005
Posts: 850
Rep Power: 4 The Dark is on a distinguished road
Thanks MBirchmeier, I didn't know about the Invoke method (I assumed that they were thread-safe - bad assumption).
I assumed that the OPs code was actually starting a new thread for each message, and this was causing the problem. I asked, but he didn't answer and he didn't post any code.
The Dark is offline   Reply With Quote
Old Feb 23rd, 2006, 8:10 AM   #7
Pedja
Programmer
 
Join Date: Nov 2005
Location: Belgrade, Serbia & Montenegro
Posts: 31
Rep Power: 0 Pedja is on a distinguished road
Send a message via ICQ to Pedja Send a message via MSN to Pedja Send a message via Yahoo to Pedja
Thanks guys, I really appreciate your efforts to help. The Dark, that's very good idea, but application still isn't working properly. When I wait for both listViews to be populated it works fine, but if I don't the program goes crazy. Some icons, even in the first listView start to disappear, strange stuff start to go on, and application breaks. For example, when I go fast, app breaks on some extension, but when I run it again, and click on the same extension, it works ok.
I don't know what to do. This is the first time I have this kind of problem.
Here is some code, maybe someone will figure out what might be the problem.
private void listView1_SelectedIndexChanged(object sender, System.EventArgs e)
{
	foreach(ListViewItem item in listView1.SelectedItems)
	{
		if (thrInfo == null)
		{
			thrInfo = new Thread( new ThreadStart(getInfo) );
			thrInfo.Start();
		}
		mut.WaitOne();
		ext="."+item.Text;
		mut.ReleaseMutex();
		areStarter.Set();
		break;
	}
}
private void getInfo()
{
	while(true)
	{
		areStarter.WaitOne();
		mut.WaitOne();
		string ext1=ext;
		mut.ReleaseMutex();
		listView2.Items.Clear();
		imageList2.Images.Clear();
		listView3.Items.Clear();
		imageList3.Images.Clear();
		appPath=RegEdit.GetAppPath(ext1);
		pictureBox1.Image=null;
		label2.Text=Shell.GetFileDesc(appPath);
		writeOpenWithList(ext1);
		if(appPath!="")
		{
			Icon ico=Shell.GetLargeIconFromFile(appPath);
			if(ico!=null) pictureBox1.Image=ico.ToBitmap();
			drawIcons();
		}
	}
}
private void writeOpenWithList(string ext1)
{
	ArrayList rez=null;
	rez=RegEdit.GetOpenWithList(ext1);
	if(rez.IndexOf(appPath)==-1) rez.Add(appPath);
	foreach(object o in rez)
	{
		if(o.ToString().Trim()!="")
		{
			Icon ico=Shell.GetSmallIconFromFile(o.ToString());
			if(ico!=null)
			{
				imageList2.Images.Add(ico);
				listView2.Items.Add(Shell.GetFileDesc(o.ToString()),imageList2.Images.Count-1);
			}
                  }
	}
}
private void drawIcons()
{
	Icon[] iconList=Shell.IconsFromFile(appPath);
	foreach(Icon icon in iconList)
		if(icon!=null) imageList3.Images.Add(icon);
	for(int i=0;i<imageList3.Images.Count;i++) listView3.ItemsAdd("",i);
}

I've tried with Control.Invoke(delegate) method too, but it doesn't help.
Pedja is offline   Reply With Quote
Old Feb 23rd, 2006, 5:54 PM   #8
The Dark
Expert Programmer
 
Join Date: Jun 2005
Posts: 850
Rep Power: 4 The Dark is on a distinguished road
Whe you tried with the Control.Invoke method, did you change all of the calls to us Invoke? This means any call in the second thread to a method of listView2, listView3, pictureBox1, imageList2, imageList3 and label2.

Can you zip up your project and attach it, so I can see the behaviour? If you don't want to post it here, PM me and I'll send you my email address.
The Dark is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 11:46 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC