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
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:
- Gather all Clients with Emails that have an Inventory Quantity > TriggerAmount and Email Them
- 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.
- 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:
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:
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