Solved

System.Threading.ThreadStateException error...?

Posted on 2008-10-15
12
795 Views
Last Modified: 2012-06-27
Hi experts

I am lost when trying to understand why this error is being thrown. I wasn't aware I was using a worker thread in my code. Please could somebody help me understand what is happenning...

I have two forms, called for arguments sake 'Main' and 'Explorer'

When a button is clicked in 'Explorer' it calls a public sub in 'Main' called SendReceive() which executes, as the name suggests, a sendreceive in the outlook namespace. It also sets up a handler for the syncend() event, called private sub SyncEnd(). When this event is called it removes the handler, kills the outlook.exe process then calls a sub in the 'Explorer' form to refresh it's view of the folder.

It's this last call that throws the exception. I am left assuming that the synend handler is being run in a seperate thread. If that is so, how do i call back to the 'Explorer' Form?

See code below.

Any help is appreciated. Thanks in advance.
'

' Main routines

'

Public Sub SendReceive()

	Try

		oApp = New Outlook.Application

		Dim oNS As Outlook._NameSpace = oApp.GetNamespace("mapi")

		Dim oSyncs As Outlook.SyncObjects

		Dim oSync As Outlook.SyncObject

		oNS.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).GetExplorer()

		oSyncs = oNS.SyncObjects

		oSync = oSyncs.Item("All Accounts")

		instance = oSync

		AddHandler instance.SyncEnd, AddressOf SyncEnd

		oSync.Start()

		oNS.Logoff()

		oSync = Nothing

		oSyncs = Nothing

		oNS = Nothing

		oApp = Nothing

	Catch ex As Exception

		MsgBox(ex.ToString)

	End Try

End Sub
 

Private Sub SyncEnd()

	RemoveHandler instance.SyncEnd, AddressOf SyncEnd

	oApp = Nothing

	Dim myProcesses As Process() = Process.GetProcessesByName("outlook")

	For Each myProcess As Process In myProcesses

		If myProcess.MainWindowTitle = "" Then myProcess.Kill()

	Next myProcess

	If Explorer.Visible = True Then Explorer.RefreshItems()

End Sub
 
 

'

' Explorer routines

'

Private Sub SendReceive(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tssendreceive.Click

	main.SendReceive()

End Sub
 

Public Sub RefreshItems()

	Dim session As RDOSession = CreateObject("Redemption.RDOSession")

	session.Logon()

	folderitems = GetMailItemsFromFolder(session.GetFolderFromPath(currentfolder))

	UpdateItems(SelectRows(sortcolumn & " " & sortorder))

	session.Logoff()

End Sub

Open in new window

0
Comment
Question by:jerute
  • 7
  • 4
12 Comments
 
LVL 2

Expert Comment

by:wmestrom
ID: 22720777
If you like to be certain that the code executes on the same thread as the thread that is associated with a form you can use the Invoke method of the form. Pass the method you want to execute on that thread as a delegate and optionally the parameters it needs.
'A delegate

Public Delegate Sub Refresh()
 

'The explorer form

Dim explorerForm As System.Windows.Forms.Form
 

'Method calling the Refresh method on the explorer form thread

Sub CallExplorerRefreshMethod

  explorerForm.Invoke([Delegate].CreateDelegate(GetType(RefreshDelegate), explorerForm, "Refresh"))
 

End Sub

Open in new window

0
 

Author Comment

by:jerute
ID: 22722523
Hi wmestrom

Thanks for your reply but I'm not sure if this would work. I neglected to mention that the explorer form remains open while the sendreceive runs.

Therefore the line 'dim explorerform as system.windows.forms.form' is going to cause a second instance, isn't it?

Jerute
0
 
LVL 2

Expert Comment

by:wmestrom
ID: 22723687
Hi jerute,

it will only create a instance if you assign it a 'New ExplorerForm' (or whatever name you gave it). However you can also just use a reference to your already open form (the Explorer variable in your code that is I think).

Just note that the method will only be called when the window is handling messages. Put simply, when it is responding to clicks, it will also execute methods send to it using Invoke().

I hope this answers your questions.

Willem
0
 

Author Comment

by:jerute
ID: 22723764
As clear as mud. I am sorry  wmestrom but when it comes to delegates I am, in the official terminology, an idiot. I really don't understand them.

Which of these bits goes where?

Thanks for your patience

Jerute
0
 
LVL 2

Expert Comment

by:wmestrom
ID: 22724015
Hi Jerute,

I copied your code and if I understand it correctly you just have to change two things. I added a delegate definition and changed the call to the RefreshItems method. This should do the trick.

Willem
    '

    ' Main routines

    '

    Public Sub SendReceive()

        Try

            oApp = New Outlook.Application

            Dim oNS As Outlook._NameSpace = oApp.GetNamespace("mapi")

            Dim oSyncs As Outlook.SyncObjects

            Dim oSync As Outlook.SyncObject

            oNS.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).GetExplorer()

            oSyncs = oNS.SyncObjects

            oSync = oSyncs.Item("All Accounts")

            instance = oSync

            AddHandler instance.SyncEnd, AddressOf SyncEnd

            oSync.Start()

            oNS.Logoff()

            oSync = Nothing

            oSyncs = Nothing

            oNS = Nothing

            oApp = Nothing

        Catch ex As Exception

            MsgBox(ex.ToString)

        End Try

    End Sub
 

    'ADDED: Define a delegate

    Public Delegate Sub Refresh()
 

    Private Sub SyncEnd()

        RemoveHandler instance.SyncEnd, AddressOf SyncEnd

        oApp = Nothing

        Dim myProcesses As Process() = Process.GetProcessesByName("outlook")

        For Each myProcess As Process In myProcesses

            If myProcess.MainWindowTitle = "" Then myProcess.Kill()

        Next myProcess

        If Explorer.Visible = True Then

            'CHANGED: Use Invoke instead of a direct call

            Explorer.Invoke([Delegate].CreateDelegate(GetType(RefreshDelegate), Explorer, "RefreshItems"))

        End If

    End Sub
 
 

    '

    ' Explorer routines

    '

    Private Sub SendReceive(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tssendreceive.Click

        Main.SendReceive()

    End Sub
 

    Public Sub RefreshItems()

        Dim session As RDOSession = CreateObject("Redemption.RDOSession")

        session.Logon()

        folderitems = GetMailItemsFromFolder(session.GetFolderFromPath(currentfolder))

        UpdateItems(SelectRows(sortcolumn & " " & sortorder))

        session.Logoff()

    End Sub

Open in new window

0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:jerute
ID: 22724426
No, doesn't work. I have an issue with your code, and this may be the problem. I refer to the line

Explorer.Invoke([Delegate].CreateDelegate(GetType(RefreshDelegate), Explorer, "RefreshItems"))

My IntelliSense tells me that refreshdelegate is not defined. I assumed (maybe incorrectly) that this should be the same as the

Public Delegate Sub Refresh()

which, incidentally, I have had to change due to it being the same as form.refresh. Lets assume, for arguments sake, that I have put

Public Delegate Sub RefreshView().

One way or another I am still getting the exceptions thrown by this call...

A first chance exception of type 'System.Threading.ThreadStateException' occurred in System.Windows.Forms.dll
A first chance exception of type 'System.InvalidOperationException' occurred in wbcontrol.exe

I'm glad you are more patient than me. LOL. Thank you.
0
 
LVL 2

Accepted Solution

by:
wmestrom earned 500 total points
ID: 22724680
Hi,

your right I miss typed. Where I wrote

Public Delegate Sub Refresh()

I meant

Public Delegate Sub RefreshDelegate()

But that doesn't help you I see. From the exception you get it seems that the Explorer thread is in MTA appartment state which doesn't allow the OLE calls you are doing. But this would mean you get the same exception if you call RefreshItems directly from the Explorer.SendReceive method.

If this is the case change the appartment state of the Explorer thread to STA:

thread.SetApartmentState(Threading.ApartmentState.STA)

If you do not start a new thread yourself than add this attribute to your startup method (usually main)

    <STAThread()> _
    Sub Main()
        'Do your stuff
    End Sub

Hope this helps...
0
 

Author Comment

by:jerute
ID: 22724797
I don't use sub main(), so where should this go?

my entry point is form_load...
0
 

Author Comment

by:jerute
ID: 22724873
oh, and

thread.SetApartmentState(Threading.ApartmentState.STA)

isn't recognised by intellisense...

0
 

Author Comment

by:jerute
ID: 22725045
but

thread.CurrentThread.SetApartmentState(Threading.ApartmentState.STA)

is. It didn't, however, solve the problem.

:(

I hate losing...
0
 

Author Closing Comment

by:jerute
ID: 31506265
Hi wmestrom

Although I still haven't found a solution to this problem I have assigned the points to you for your participation.

Thanks again.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

This article explains how to create and use a custom WaterMark textbox class.  The custom WaterMark textbox class allows you to set the WaterMark Background Color and WaterMark text at design time.   IMAGE OF WATERMARKS STEPS Create VB …
Article by: Kraeven
Introduction Remote Share is a simple remote sharing tool, enabling you to see, add and remove remote or local shares. The application is written in VB.NET targeting the .NET framework 2.0. The source code and the compiled programs have been in…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now