threading

M

Mike P

I have two loops that I am running, one of which is calling a method
which contains the second one. What I want to happen is for when the
second loop exits (the inner loop), then the code will return to the
first loop (the outer loop) and continue executing, but this doesn't
seem to be happening. Here is my code if anybody can help me out :

Loop 1 :

for (int i = 0; i < oItems.Count; i++)
{
oItems = oInbox.Items;

ProgressWindow progress = new ProgressWindow();
progress.Text = "Work";
System.Threading.ThreadPool.QueueUserWorkItem(new
System.Threading.WaitCallback(MoveMail), new object[] { progress,
oItems, destfolder, objConn });

if (oItems.Count == 1)
{
i = -1;
}
else
{
i = 0;
}

progress.ShowDialog();
}

Loop 2 in MoveMail method :

private void MoveMail(object status)
{
object[] parms = (object[])status;
IProgressCallback callback = (IProgressCallback)parms[0];
Outlook.Items col = (Outlook.Items)parms[1];
Outlook.MAPIFolder destfolder =
(Outlook.MAPIFolder)parms[2];
SqlConnection objConn = (SqlConnection)parms[3];

string strInsertQuery = "";

callback.Begin(0, col.Count);

for (int i = 1; i < col.Count + 1; i++)
{
callback.SetText(String.Format("Performing op: {0}",
i));
callback.StepTo(i);

if (callback.IsAborting)
{
return;
}

if (col.Item(i) is Outlook.MailItem)
{
Outlook.MailItem oMsg =
(Outlook.MailItem)col.Item(i);

//subject
if ((oMsg.Subject == null) || (oMsg.Subject == ""))
{
oMsg.Subject = "blank";
}
else
{
//escape quotes in subject
oMsg.Subject = oMsg.Subject.Replace("'", "''");
}

if (oMsg.ReceivedByName == null)
{
if (oMsg.SenderName.IndexOf("'") != -1)
{
string strSenderName =
oMsg.SenderName.Replace("'", "''");

strInsertQuery = "INSERT INTO SeedingResults
(SenderName, ReceivedTime, Subject) VALUES ('" + strSenderName + "','" +
oMsg.ReceivedTime.ToString() + "','" + oMsg.Subject.ToString() + "')";
}
else
{
strInsertQuery = "INSERT INTO SeedingResults
(SenderName, ReceivedTime, Subject) VALUES ('" +
oMsg.SenderName.ToString() + "','" + oMsg.ReceivedTime.ToString() +
"','" + oMsg.Subject.ToString() + "')";
}
}
else
{
if (oMsg.SenderName.IndexOf("'") != -1)
{
string strSenderName2 =
oMsg.SenderName.Replace("'", "''");

strInsertQuery = "INSERT INTO SeedingResults
(ReceivedByName, SenderName, ReceivedTime, Subject) VALUES ('" +
oMsg.ReceivedByName.ToString() + "','" + strSenderName2 + "','" +
oMsg.ReceivedTime.ToString() + "','" + oMsg.Subject.ToString() + "')";
}
else
{
strInsertQuery = "INSERT INTO SeedingResults
(ReceivedByName, SenderName, ReceivedTime, Subject) VALUES ('" +
oMsg.ReceivedByName.ToString() + "','" + oMsg.SenderName.ToString() +
"','" + oMsg.ReceivedTime.ToString() + "','" + oMsg.Subject.ToString() +
"')";
}
}

//write to database
SqlCommand objInsertCommand = new
SqlCommand(strInsertQuery, objConn);

try
{
objInsertCommand.ExecuteNonQuery();
}
catch
{
throw new DatabaseException();
}

oMsg.Move(destfolder);

if (callback.IsAborting)
{
return;
}
}
}
}
 
J

Jon Skeet [C# MVP]

I have two loops that I am running, one of which is calling a method
which contains the second one. What I want to happen is for when the
second loop exits (the inner loop), then the code will return to the
first loop (the outer loop) and continue executing, but this doesn't
seem to be happening. Here is my code if anybody can help me out :

There's a lot of code there, most of it not relevant to the problem.

Could you post a short but complete program that demonstrates the
problem?
See http://pobox.com/~skeet/csharp/complete/html for more on what I
mean by this.

Jon
 
M

Mike P

Here's the code cut down :

Loop 1 :

for (int i = 0; i < oItems.Count; i++)
{
oItems = oInbox.Items;

ProgressWindow progress = new ProgressWindow();
progress.Text = "Work";
System.Threading.ThreadPool.QueueUserWorkItem(new
System.Threading.WaitCallback(MoveMail), new object[] { progress,
oItems, destfolder, objConn });

if (oItems.Count == 1)
{
i = -1;
}
else
{
i = 0;
}

progress.ShowDialog();
}

Loop 2 :

private void MoveMail(object status)
{
object[] parms = (object[])status;
IProgressCallback callback = (IProgressCallback)parms[0];
Outlook.Items col = (Outlook.Items)parms[1];
Outlook.MAPIFolder destfolder =
(Outlook.MAPIFolder)parms[2];
SqlConnection objConn = (SqlConnection)parms[3];

string strInsertQuery = "";

callback.Begin(0, col.Count);

for (int i = 1; i < col.Count + 1; i++)
{
callback.SetText(String.Format("Performing op: {0}",
i));
callback.StepTo(i);

if (callback.IsAborting)
{
return;
}

if (col.Item(i) is Outlook.MailItem)
{
Outlook.MailItem oMsg =
(Outlook.MailItem)col.Item(i);

//db stuff goes here

oMsg.Move(destfolder);

if (callback.IsAborting)
{
return;
}
}
}
}
 
M

Mike P

Here's another attempt :

Main form :

Outlook.Application oApp = new Outlook.ApplicationClass();

Outlook.NameSpace oNS = oApp.GetNamespace("mapi");

string strUserName =
System.Environment.UserName.ToString();

oNS.Logon(strUserName, System.Reflection.Missing.Value, false, true);

Outlook.MAPIFolder oInbox =
oNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);

Outlook.MAPIFolder destfolder =
oInbox.Folders.Item("Archive");

Outlook.Items oItems = oInbox.Items;

int intTotalNumberOfItemsInInbox = oItems.Count;

for (int i = 0; i < oItems.Count; i++)
{
oItems = oInbox.Items;

ProgressWindow progress = new ProgressWindow();
progress.Text = "Work";
System.Threading.ThreadPool.QueueUserWorkItem(new
System.Threading.WaitCallback(MoveMail), new object[] { progress,
oItems, destfolder, objConn });

if (oItems.Count == 1)
{
i = -1;
}
else
{
i = 0;
}

progress.ShowDialog();
}

private void MoveMail(object status)
{
object[] parms = (object[])status;
IProgressCallback callback = (IProgressCallback)parms[0];
Outlook.Items col = (Outlook.Items)parms[1];
Outlook.MAPIFolder destfolder =
(Outlook.MAPIFolder)parms[2];
SqlConnection objConn = (SqlConnection)parms[3];

callback.Begin(0, col.Count);

for (int i = 1; i < col.Count + 1; i++)
{
callback.SetText(String.Format("Performing op: {0}",
i));
callback.StepTo(i);

if (callback.IsAborting)
{
return;
}

if (col.Item(i) is Outlook.MailItem)
{
Outlook.MailItem oMsg =
(Outlook.MailItem)col.Item(i);

oMsg.Move(destfolder);

if (callback.IsAborting)
{
return;
}
}
}
}

Added to this I am using ProgressWindow.cs and IProgressCallback.cs from
the following example :

http://www.codeproject.com/cs/miscctrl/progressdialog.asp

HTH,

Mike
 
M

Mike P

The problem I have with this code is that without the threading code,
after it has completed executing inside the second loop it returns to
the first loop, whereas when the threading code is added, it doesn't.
Here's the code in it's original form :

for (int i = 0; i < oItems.Count; i++)
{
oItems = oInbox.Items;

MoveMail(oItems, destfolder, objConn);

if (oItems.Count == 1)
{
i = -1;
}
else
{
i = 0;
}

private void MoveMail(Outlook.Items col, Outlook.MAPIFolder destfolder,
SqlConnection objConn)
{

for (int i = 1; i < col.Count + 1; i++)
{


if (col.Item(i) is Outlook.MailItem)
{
Outlook.MailItem oMsg =
(Outlook.MailItem)col.Item(i);

oMsg.Move(destfolder);
}
}
}
 
J

Jon Skeet [C# MVP]

Here's another attempt :

*Please* read the link I gave.

Try to cut and paste the code you've posted directly into an empty
file and compile it. It won't work, because it's incomplete.

Also, anything which requires Outlook isn't going to be easy for
everyone to reproduce. Put in some dummy work (eg writing a counter to
the console). This isn't meant to be real code, it's meant to be code
which *just* demonstrates the problem.

Jon
 
W

Willy Denoyette [MVP]

Mike P said:
Here's another attempt :

Main form :

Outlook.Application oApp = new Outlook.ApplicationClass();

Outlook.NameSpace oNS = oApp.GetNamespace("mapi");

string strUserName =
System.Environment.UserName.ToString();

oNS.Logon(strUserName, System.Reflection.Missing.Value, false, true);

Outlook.MAPIFolder oInbox =
oNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);

Outlook.MAPIFolder destfolder =
oInbox.Folders.Item("Archive");

Outlook.Items oItems = oInbox.Items;

int intTotalNumberOfItemsInInbox = oItems.Count;

for (int i = 0; i < oItems.Count; i++)
{
oItems = oInbox.Items;

ProgressWindow progress = new ProgressWindow();
progress.Text = "Work";
System.Threading.ThreadPool.QueueUserWorkItem(new
System.Threading.WaitCallback(MoveMail), new object[] { progress,
oItems, destfolder, objConn });

if (oItems.Count == 1)
{
i = -1;
}
else
{
i = 0;
}

progress.ShowDialog();
}

private void MoveMail(object status)
{
object[] parms = (object[])status;
IProgressCallback callback = (IProgressCallback)parms[0];
Outlook.Items col = (Outlook.Items)parms[1];
Outlook.MAPIFolder destfolder =
(Outlook.MAPIFolder)parms[2];
SqlConnection objConn = (SqlConnection)parms[3];

callback.Begin(0, col.Count);

for (int i = 1; i < col.Count + 1; i++)
{
callback.SetText(String.Format("Performing op: {0}",
i));
callback.StepTo(i);

if (callback.IsAborting)
{
return;
}

if (col.Item(i) is Outlook.MailItem)
{
Outlook.MailItem oMsg =
(Outlook.MailItem)col.Item(i);

oMsg.Move(destfolder);

if (callback.IsAborting)
{
return;
}
}
}
}

Added to this I am using ProgressWindow.cs and IProgressCallback.cs from
the following example :

http://www.codeproject.com/cs/miscctrl/progressdialog.asp

HTH,

Mike



It's really hard to see what's going on, but I would suggest you to forget about a
threadpool thread, use a 'normal auxiliary' thread and initialize this one to enter the STA.
This way you are creating the COM reference on the main UI thread (STA) and you pass COM
references to a worker thread taken from the pool, however these pool threads are running in
the MTA, that means that you probably have to deal with marshaling issues between the MTA
and the STA thread.
Another option is to create the "Outlook" instance from the auxiliary thread, don't
initialize it to enter an STA, should work from MTA.

Willy.
 
W

Willy Denoyette [MVP]

Mike P said:
Willy,

Apologies, I'm new to threading, but what do you mean by MTA?


Thanks,

Mike


STA and MTA are COM acronyms that stand for Single Threaded Apartment and Multi Threaded
Apartment respectively. Apartments define synchronization context boundaries and are mainly
used by COM clients to help them to solve synchronization issues when calling across these
boundaries.
All Windows Forms (and WPF) applications are tied to the COM Threading Model, that is, a UI
thread needs to enter a STA to function properly. And each call into COM must be done from a
COM initialized thread, that is, each such thread must have joined the MTA (one per process)
or must have initialized the thread to enter a STA (max. one per thread).

There is a lot of COM information available in MSDN, you shouldn't read all of it ;-), start
slowly, but at least try to get some basic knowledge about this technology.

Here is something that should give you a head start :
http://blogs.msdn.com/larryosterman/archive/2004/04/28/122240.aspx

Msdn contains more detailed information...
http://msdn2.microsoft.com/en-us/library/ms680112.aspx
http://msdn2.microsoft.com/en-us/library/ms693421.aspx

Just as there are some good articles available on codeproject:

http://www.codeproject.com/com/CCOMThread.asp
http://www.codeproject.com/com/CCOMThread2.asp


WIlly.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Threading guru required! 7

Top