My.Resources and Threading..

JaedenRuiner

Well-known member
Joined
Aug 13, 2007
Messages
340
Programming Experience
10+
How can I make the My.Resources module thread static?

They are all constants, never going to be changed, and frankly they can't be changed. all my resource strings are Readonly Properties, but for some reason (and I'm sure there is one) I cannot access my.Resources from within a worker thread. I want to be able to access them anywhere and everywhere without having to mess around with invoking.

Invoking will work, naturally, but there isn't an easy way to invoke to a property. I'd have to write an accessor function that would simply return the property value.

basically, i would prefer to write something, modifying the my.resources module to be fully thread safe so I can access it anywhere no matter what and won't get these darned "Thread Invoke Exceptions"

Thanks
 
Perhaps you can give a basic example that reproduce the issue as I can't see the problem you're describing, something along the lines of "dim s as string = my.resources.mystring" if possible.

As for invoking and shared, My.Resources is defined in a Module, so they are Shared, and Shared members is same for all instances and all threads.
 
As everyone should do every time, I think you need to explain exactly what you're trying to achieve, exactly what you're doing and exactly what happens because I can access properties of My.Resources without issue in a secondary thread.
 
Well, I'll do my best. I mean I am dealing with several thousand lines of code here, and buried in the middle is the crux of the problem. Basically, I have designed a Command structure

VB.NET:
Public MustInherit Class FlufCommand

public mustoverride Function Execute(Optional Arg as Object = Nothing) as Boolean

From this I base all my process commands that are expected to be process intensive. The application is the same Database app that all my questions have revolved around for the past 6 months, and it simply processes and manipulates Excel files for shipment transactions, and generates an automatic email to clients upon specific conditions being met.

Currently, (thanks to JohnH this past week) i realized that this Linq thing was way cooler than I had originally investigated. I had incidentally included it in my project, I think due to some random incursion that often happens around me, and often used some of its extensibility without really knowing what it was. Now that I know, I do feel mild chagrin, if it wasn't masked by my overwhelming geekdom on how cool and awesome it's power is. Arrays, Collections, datasets, you name it, I can just grab an enumerable collection based on near-SQL style commands and process them.

Though, that isn't the source of my problem, it is helpful to understand that I was ripping up the foundation of one of my Command objects, RexamEmail(), which is the primary object responsible for gathering which entries in the database are ready to be emailed, formatting the HTML email bodies, and sending them off via System.Net.Mail. We had come across the need to manually enact that which was originally programmed to be calculated during the automation process, and while in the middle of this process I discovered an implemented the use of Linq for this process as an initial testing ground for my future projects.

The idea has three stages:
  1. Gather all Clients with Emails that have an Inventory Quantity > TriggerAmount and Email Them
  2. If the Argument is a ClientRow, then find the currently existing Email Entry (that has not yet met the TriggerAmount) and Close it off and send it regardless.
  3. If the Argument is a MailboxRow, then re-send the email, just in case it was lost in initial transit or the client can't find it.
Initially, Stage 1 was being executed via my SqlParser class, executing a SQL command against the database and retrieving the results into a temporary datatable as a list of Customer IDs. Since I was already doing some work with the dataset, i decided not to mix my technologies, and use linq against the dataset to provide my customer list, as well this allowed me to reduce the profile of the object, since i could program the inner methods to work directly from a ClientRow object, thus, if the singular argument passed to the command object was a ClientRow it would behave as normal, otherwise for Nothing, it would just Loop through the enumerated ClientRows.

Now for the actual Source of the issue:
In all of this, since I have easily 4-5 different Command style objects that perform various functions, I have had designed for some time a ProgressForm, which is designed to initialize a progress meter (a personal modified component that has a more appealing marquee mode and a text % in the middle of continuous mode) and a looping timer to keep it waiting.

the Form accepts a FlufCommand object, and has a CommandArgument as Object property. So it works something like this:
VB.NET:
ProgForm = New ProgressForm
ProgForm.Show(Me)
ProgForm.CommandArgument = ??
ProgForm.Command = ??
' ProgForm.Wait
The last, the Wait command is a simple loop that continuously executes a Applicaiton.DoEvents as it polls for command completion. Sometimes it is necessary to wait for the progressform's command to complete execution, sometimes the entire op can be completely asynchronous. The Progform does trigger it's own event when the command completes, so there are many ways to manipulate this.

IN the Progress form, the FlufCommand object itself is Executed from within a BackgroundWorker Thread object, and all my problems are occuring at this point, and they definitely seem to be related to the threading issue, however, I was not seeing them before I altered my RexamEmail() object so I am not sure what could be causing the issue.

there are two main issues:

VB.NET:
   Private Sub WorkThread_ProgressChanged(ByVal sender As Object, _
                  ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
                  Handles WorkThread.ProgressChanged
      Dim cmd As FlufLib.FlufCommand = DirectCast(e.UserState, FlufLib.FlufCommand) _
                'The UserState is the Command Being Executed
      Dim stat As String = ""
      If cmd.IsValid Then 'extension that tests the validity of specific objects
         If cmd.ProgressEnabled AndAlso cmd.ProgressArgs.IsValid Then _
         ' Make sure we are in progress mode if the command supports and we aren't already
            If _status > 1 Then Me.BeginInvoke(New _
                 SetStyleCallBack(AddressOf SetProgStyle), cmd) 
                     'sets the marquee/continuous mode of progressbar
            If cmd.ProgressArgs.Status IsNot Nothing Then
               With cmd.ProgressArgs
                  If TypeOf .Status Is String Then
                     stat = .Status.ToString
                  ElseIf TypeOf .Status Is FlufCommand.ProgressStatusStruct Then
                     Dim pss As FlufCommand.ProgressStatusStruct = _
                         CType(.Status, FlufCommand.ProgressStatusStruct)
                     Dim strs As String = _
                        CType(Me.EndInvoke(Me.BeginInvoke( _
                            New System.Func(Of String, Object)(AddressOf My.Application.Resource), _
                            "EmailStatus")), String)
                     If strs.IsValid AndAlso TypeOf cmd Is RexamDatabase.RexamEmail Then _
                            stat = strs.Split(";"c)(pss.Status)

[b][I]'before the above 2 lines were 1:[/I] 
    'stat = My.Resources.EmailStatus.Split(";"c)(pss.status)[/b]

                     stat &= If(pss.Message.IsValid, ": " & pss.Message, "") & "..."
                  End If
               End With
            End If
         End If
      End If
      Me.BeginInvoke(New _
             SetProgressDelegate(AddressOf SetProgress), e.ProgressPercentage, stat)
   End Sub

   'this is the MyCommand Event that triggers the WorkThread progress 
   'reporting my thread awareness is raising red flags here and i'm working to
   'revamp the idea, instead of sending the sender object across thread lines,
   'but so far I haven't had any problems
   Private Sub _cmd_Progress(ByVal sender As Object, ByVal e As FlufLib.ProgressEventArgs) _
           Handles _cmd.Progress
      WorkThread.ReportProgress(e.Percent, sender)
   End Sub

this part is I think working better now, as I wrote a global function in ApplicationEvents.vb to extend the application object, which I simply use a delegate on the invoke to retrieve the property value of the My.Resources module. Originally (in the bolded line above) accessing that property was causing a ThreadInvoke Exception every time. During Thread execution when debugging (stepping) inside the worker thread space, ie, i step into the RexamEmail Object, the My.Resources.WhateverResourcePropertyHere ALWAYS shows a value of an exception in the Watch window. I don't know why my system is acting differently on thread safe access but regardless of what it should be doing it is causing an error for me *shrug*.

The other problem is of a similar nature (deals with threading and the RexamEmail object)...but perhaps I should post that one in a different thread here, as it has to deal with RaceOnRCWClose (which makes no sense to me since there is only one place where I access/deal with Word Automation and that is in the RexamEmail object, within one method, so no other method could or should be trying to access the object with I close the document.) But more on that later. Perhaps some of my threading concerns are causing the current issue, but perhaps it is something else entirely.
Thanks
 
Last edited:
Well,

it appears that the initial source of all my issues was here:
VB.NET:
Private Sub _cmd_Progress(ByVal sender As Object, ByVal e As FlufLib.ProgressEventArgs) _
           Handles _cmd.Progress
      WorkThread.ReportProgress(e.Percent, sender)
   End Sub
accessing the same object across thread lines was just causing some severe issues, so I've revamped the entire communication between Command Object and progress events so that an internal class retains all possible data, and is cloned instead of passed. This prevents the main thread from inadvertently mucking things up when trying to access the same object that exists in the secondary thread. I still have resolved why the My.Resources object has issues across thread lines, but by turning on the "Stop on thrown" exception handler in the debugger for all System level exceptions has helped to narrow down quite a few other issues, where exceptions were causing issues, but i was technically "handling" them, if not handling them well, :D.

Now on to further problems and enigmas, thanks for the help..
 
You should note that My.Resources is not an object. It is a namespace. When you access properties of My.Resources you are actually accessing properties of the Resources module that is a member of that namespace. When you get My.Resources.SomeResource you are actually getting My.Resources.Resources.SomeResource but, as we know, module names can be omitted when accessing their members, unlike class names.

Also, when you get a resource twice you are NOT getting the same object. Each time you get a property from My.Resources it goes to the resource file, extracts the resource data and creates a new object. That means that if you get the same resource twice you will be creating two different objects. You can prove this for yourself by executing code like this:
VB.NET:
MessageBox.Show((My.Resources.MyImage Is My.Resources.MyImage).ToString())
You'll find that that shows False, indicating that the two references point to two different objects.

Now, why this should cause an issue when accessing the same resource from two different threads I don't actually know, but it's worth noting that creating multiple objects from the same resource is generally best avoid for efficiency's sake anyway. There will obviously be times where it's the right thing to do but I don't think your case is one of them.
 
Your UI thread creates a ProgressForm which calls bgw.RunWorkerAsync(), bgw.DoWork event calls the command.work(), which reports progress by calling bgw.ReportProgress(), now back on UI thread in bgw.ProgressChanged event you can't get My.Resources.theString ?
I also elevated this one level by using a new thread to create the ProgressForm, then same processing, but still no issues with the My.Resources.
Was this close to what you tried to describe?
 
Your UI thread creates a ProgressForm which calls bgw.RunWorkerAsync(), bgw.DoWork event calls the command.work(), which reports progress by calling bgw.ReportProgress(), now back on UI thread in bgw.ProgressChanged event you can't get My.Resources.theString ?

Yup. Every time i do I get a threadInvoke Exception thing. And when I try to view the resource via the watch window, while stepping through bgw.ProgressChanged it lists it as an exception in the watch window.
 
Back
Top