|
As mentioned, the easiest way is to use a print driver and print the document to the PDF printer. I use doPDF[^] because it's easy, free and works great.
You can essentially print anything to a PDF file, whether it's from Word or something else.
CQ de W5ALT
Walt Fair, Jr., P. E.
Comport Computing
Specializing in Technical Engineering Software
|
|
|
|
|
I am attempting to write a multithreaded application.
The idea is to have a scripted exe run that takes a list of devices, strips out one device and its information into an array, creates a new object of a class. which contains a backgroundworker that contains a telnet object, to telnet to said device and perform simple work (just login at this time). I have 100 device I am doing this for.
So far, everything looks like it works. All 100 devices have a new class object and bgw worker created for them. My problem lies in how to determine when those have completed their work. This is a problem because the rest of the exe needs to process the results of my simple work but can only be done after all have completed.
How can I tell when the work is done? I was thinking about writing something into the RunWorkerCompleted event that tallys but that seems like not the best idea.
-Steve
|
|
|
|
|
So, if all of your work is being done in these worker threads, then your main application just becomes a thread manager. Right? (lets take that as true)
1) The thread manager keeps an array of status; one slot for each thread dispatched. (running/WorkDone)
2) Each thread should throw an event which represents "WorkDone"; the thread manager will
listen for this event and update the corresponding array slot.
3) When all threads have been dispatched and begin their work, the thread manager just loops around and sleeps periodically checking if all status array slots have a "WorkDone" status; when that occurs you can continue with you process.
Maybe extend you status array to have a start time so that you can determine if a thread is taking too long, you can take corrective action.
Just a thought.
Good luck.
|
|
|
|
|
So, if all of your work is being done in these worker threads, then your main application just becomes a thread manager. Right? (lets take that as true)
Yes
2) Each thread should throw an event which represents "WorkDone"; the thread manager will
listen for this event and update the corresponding array slot.
How can I make the thread throw a custom event? Conversly, how do I make the thread manager listen?
|
|
|
|
|
OK so I attempted it and I think I ran into an issue with scope.
In the worker thread, I created an event
Public Event IamDone(ByVal sender As Object, ByVal IP As String)
I call that event in the RunWorkerCompleted sub
RaiseEvent IamDone(Me, "111")
And now I am trippng up on how to create the handler in my thread manager. I assumed it would be
AddHandler myworkerthreadclass.IamDone, Addressof myworkerthreadclass.IamDone
but that doenst work
|
|
|
|
|
Why don't use just use the RunWorkerCompleted event, instead of creating a new event to raise? In RunWorkerCompleted you should be able to get the key to the device, and use that to update your status collection.
Or if you are on .NET 4.0 you can use one of the parallel collection to handle this much cleaner.
|
|
|
|
|
Hi,
1.
Yes, you want the RunWorkerCompleted event handler to do something your main thread can notice. Lots of things are possible, here is one example:
have a global variable that is initialized to the number of jobs, and gets decremented (see Interlocked.Decrement) by each RunWorkerCompleted handler, which on top of that, by checking for zero, raises a signal (see e.g. ManualResetEvent)
2.
It is a bad idea to launch many BackGroundWorkers, as that would be too expensive. The better approach would be to have:
- a small number of BackGroundWorkers (say two times the number of cores your system has, Environment.ProcessorCount);
- a queue of jobs, filled by the main thread, and queried by those BackGroundWorkers (use a lock!);
- and probably a queue of results, filled by those BackGroundWorkers (use another lock), and emptied by the main thread when it gets its "all done" signal.
|
|
|
|
|
OK so I attempted it and I think I ran into an issue with scope.
In the worker thread, I created an event
Public Event IamDone(ByVal sender As Object, ByVal IP As String)
I call that event in the RunWorkerCompleted sub
RaiseEvent IamDone(Me, "111")
And now I am trippng up on how to create the handler in my thread manager. I assumed it would be
AddHandler myworkerthreadclass.IamDone, Addressof myworkerthreadclass.IamDone
but that doesn't work
|
|
|
|
|
svanwass wrote: that doesn't work
|
|
|
|
|
|
people here are unlikely to download anything just to maybe understand a question; what you should do is explain the symptoms ("doesn't work" tells us nothing) and tell how it differs from your expectations, then ask a specific question and/or show a relevant code snippet.
|
|
|
|
|
My apologies.
Main Form
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim passing(0, 3) As String
passing(0, 0) = "10.1.1.1"
passing(0, 1) = "10.1.1.2"
passing(0, 2) = "10.1.1.3"
passing(0, 3) = "10.1.1.4"
Dim myworker As New Foo(passing)
End Sub
Private Sub IHeardThat() Handles Foo.IamDone
'this does NOT work
End Sub
End Class
My Foo class
Imports System.ComponentModel
Imports System.IO
Imports System.Threading
Public Class Foo
Public WithEvents backgroundWorker1 As System.ComponentModel.BackgroundWorker
Public Event IamDone(ByVal sender As Object, ByVal IP As String)
Dim devicelist(,) As String
Dim txtFile
Public Sub New(ByVal Value As String(,))
devicelist = Value
backgroundWorker1 = New BackgroundWorker
backgroundWorker1.WorkerReportsProgress = True
backgroundWorker1.RunWorkerAsync(devicelist)
End Sub
Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork
' Get the BackgroundWorker object that raised this event.
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
Dim myargs As String(,) = e.Argument
End Sub
Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles backgroundWorker1.RunWorkerCompleted
RaiseEvent IamDone(Me, "Done")
End If
End Sub
End Class
So that's basically it. I attempted to write a sub that would handle the custom event but the form class doesnt see the foo class event.
Does that make sense?
|
|
|
|
|
does that compile at all?
svanwass wrote: Private Sub IHeardThat() Handles Foo.IamDone
The IamDone event is not static/shared, how can this work?
Dim myworker As New Foo(passing)
AddHandler myworker.IamDone, AddressOf IHeardThat
does work but is not OK as the handler is added while the BGW is alrady running, so there is a race condition.
possible solutions:
1. pass the delegate to the Foo constructor, so it can add the handler on time;
2. don't start the BGW from the Foo constructor, add a method the caller should call after having set the handler.
General advice:
1. consider making Foo inherit from BGW.
2. start every VB file with "option strict on"
3. tell Visual not to launch the app as long as there are compile errors
4. tell Visual to treat all warnings as errors
(2+3+4 will be a PITA at first, but it will soon teach you to write correct code)
|
|
|
|
|
I think the best way would be using a waithandle that signals when done. If you combine the waithandles for all processes, you can use the WaitHandle.WaitAll Method.
See this MSDN article for an example
|
|
|
|
|
Save the thread class instances that you create into an array or IList<thread>. Once you have started all of your threads, you can loop through the array or list checking either the State or IsAlive property of each thread until all indicate that they are finished.
This way, you don't have to block your main thread while waiting for the other threads to finish. Your main thread can continue working and just check periodically to see if all worker threads have finished.
Kevin Rucker, Application Programmer
QSS Group, Inc.
United States Coast Guard OSC
Kevin.D.Rucker@uscg.mil
"Programming is an art form that fights back." -- Chad Hower
|
|
|
|
|
Seems like I have a lot to learn about the proper handeling of threads. Would any of you have a recommendation on the topic but in relations to c#? I dislike VB...
|
|
|
|
|
Free e-book on C# threading:
www.albahari.com/threading/threading.pdf
Kevin Rucker, Application Programmer
QSS Group, Inc.
United States Coast Guard OSC
Kevin.D.Rucker@uscg.mil
"Programming is an art form that fights back." -- Chad Hower
|
|
|
|
|
This site has been so helpful in the past for me over 8 years. I am self taught .net, and have used it primarily with SolidWorks API since 2003.
Thread for me is simply threads, and delegates. I run threads in order to crunch data. It keep the main form thread alive while crunching the info. Many times the threads have to send stuff back to the main threaded form. In order to do this without cross thread violations, we need to use delegates.
My technique for sending info into a thread, and having the thread call back is rather simple. If I need to create lets say 100 threads. I will use a thread in order to do that. The thread that creates the 100 threads will sleep, only to awake and see if all threads have died. When all threads have died, the main thread cruches and then dies. The main form thread never stalls out, and gets tickled from all sides by all threads from a delegates sub.
Here is the code I wrote to explain the most on these topics. Things to note are as follows:
Each thread can create its node without limitations.
The main thread relys on all threads being done creation. Then the assumtion is made that all thread nodes exist. This can only be true because all threads are not alive. So the main thread changes the display name of each treenode, and then dies itself.
The threads and delegates take object arrays, so you can pack them full of anything, both in and out of the thread.
The form is very much alive through the whole process. If only a form could speak, because this one is far from a deep sleep.
Cheers,
Sean P
(Sldprt[ItsAboutTime])
Imports System.Threading
Public Class Form1
Dim t1 As Thread
Dim t2 As Thread()
Dim c As Collection
Dim treeview As TreeView
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
tMain()
End Sub
Sub tMain()
c = New Collection
treeview = New TreeView
Me.Controls.Add(treeview)
treeview.Size = New Size(200, 400)
treeview.Location = New Point(10, 6)
treeview.Visible = True
c.Add(100, "StartData")
t1 = New Thread(AddressOf MainThreading)
t1.Start(New Object() {c})
End Sub
Sub MainThreading(ByVal args As Object)
With CType(args(0), Collection)
ReDim t2(.Item("StartData"))
Dim i As Integer
For i = 0 To UBound(t2)
.Add(i, "ThreadKey" & i)
t2(i) = New Thread(AddressOf WorkerThread)
t2(i).Start(New Object() {args(0), i})
Next
Do
Dim bol As Boolean = True
For i = 0 To UBound(t2)
If t2(i).IsAlive Then
bol = False
End If
Next
If bol Then
Exit Do
End If
Thread.Sleep(100)
Loop
Dim treenode As TreeNode
For i = 0 To .Item("StartData")
With treeview.Nodes.Item("Nod" & i)
ChangeNodes(New Object() {treeview.Nodes.Item("Nod" & i), _
.Text & _
" [" & .Name & _
" of " & .Tag & "]"})
End With
Next
End With
End Sub
Sub WorkerThread(ByVal args As Object())
Dim treenode As TreeNode = New TreeNode
Dim CollectionCount As Integer = CType(args(0), Collection).Item("StartData")
Dim Name As String = CType(args(1), String)
treenode.Name = "Nod" & Name
treenode.Text = Name
treenode.Tag = CollectionCount
AddNodes(treenode)
End Sub
Public Delegate Sub AddNode(ByRef obj As Object)
Sub AddNodes(ByRef obj As Object)
If treeview.InvokeRequired Then
Dim ad As AddNode = New AddNode(AddressOf AddNodes)
treeview.Invoke(ad, obj)
Else
treeview.BeginUpdate()
treeview.Nodes.Add(obj)
treeview.EndUpdate()
End If
End Sub
Public Delegate Sub ChangeNode(ByRef obj As Object)
Sub ChangeNodes(ByRef obj As Object)
If treeview.InvokeRequired Then
Dim ad As ChangeNode = New ChangeNode(AddressOf ChangeNodes)
treeview.Invoke(ad, obj)
Else
Dim TreeNode As TreeNode = CType(obj(0), TreeNode)
TreeNode.Text = CType(obj(1), String)
End If
End Sub
End Class
Dont turn around, cause your...
modified on Thursday, November 18, 2010 1:06 AM
|
|
|
|
|
Hi,
Can anyone guide me on how to generate a listbox from one listbox?
I have a listbox1 with items such as ( sale, buy). On listbox1_TextChanged, I want listbox2 to enlist items(Old metal iron, scrap metal iron) based on the conditions..
thanks in advance
|
|
|
|
|
What have you tried? You should be able to Clear the listbox2 's Items and Add new ones according to the value of listbox1 's Text .
CQ de W5ALT
Walt Fair, Jr., P. E.
Comport Computing
Specializing in Technical Engineering Software
|
|
|
|
|
waner michaud wrote:
On listbox1_TextChanged
You are using the wrong event. Its ListBox1_SelectionChanged.
Well i don't know what your program does and how do you get these:
waner michaud wrote:
items(Old metal iron, scrap metal iron)
well on the above event, use the ListBox1.SelectedItem to get the selected item in your listbox and then you use some function and pass ListBox1.SelectedItem like :dim myListofItems= getItems(ListBox1.SelectedItem)...define this function and give its return type as List and then do this:
for each myItem as String in myListOfitems
ListBox2.items.add(myItem)
end for
|
|
|
|
|
Hello,
I'm have a VB solution with 5 projects, and I'm using some third-party controls (from the same vendor) in these projects. I'm installing all of these projects to their own folder within a main folder.
Main project folder: C:\Program Files\App Suite
Project 1 - C:\Program Files\App Suite\Project 1
Project 2 - C:\Program Files\App Suite\Project 2
etc
When I compile the Windows installer, it tries to bring in the same DLL multiple times (once for each time we use it in a project).
What I want to do is install the Dlls to a 'Common Files' folder (C:\Program Files\Common Files\App Suite) and only have one copy of each DLL. I then want to have my 5 projects reference the shared DLLs from the common folder.
Does anyone know what I need to do to make this happen? What I want to avoid is having 5 copies of the same DLL. I just want to have my 5 projects reference the same DLL file.
Thanks in advance!
|
|
|
|
|
I'd start by scraping the Setup and Deployment projects and use a third party tool, such as InstallShield Express, InnoSetup, WIX, or whatever to create the installer. These tools will let you be far more flexible than the bare-bones basic Setup and Deployment project.
|
|
|
|
|
Hmm - so it sounds like there isn't a setting that would tell a project to look for DLLs in folders outside of the project hierarchy. That poses a problem. The solution is in VB.NET 2008 and I'm doing this in a government environment, so I'm subject to those constraints. We don't have InstallShield Express - just the native Windows installer options in Visual Studio 2008.
Originally, we were dropping all of the project outputs into a single folder (ex: C:\Program Files\App Suite), but the Setup project identified the same DLLs as dependencies multiple times (once for each project using the DLL). We've tried marking Exclude on the duplicates, but that seems like a workaround and not a real solution.
I'm assuming that if we did the Setup scraper, and used InstallShield Express, we'd also need to include code in each project to manually force the project to look in the Common File folder for the 'missing' DLLs.
Thanks for the help on this, Dave, et al. It is appreciated.
|
|
|
|
|
The problem with the Setup and Deployment project is that it's not really designed to support multiple projects or complex installations. I haven't used it in years, just because I needed to to more complex things in my setups, like what you're doing.
vvincent wrote: We've tried marking Exclude on the duplicates, but that seems like a workaround and not a real solution.
There's a bunch of little gotcha's in the S&D project like that.
vvincent wrote: I'm assuming that if we did the Setup scraper, and used InstallShield Express, we'd also need to include code in each project to manually force the project to look in the Common File folder for the 'missing' DLLs.
I've had to do that before. It's not very difficult at all, but you do have to put some custom code in to use the CommonFiles folder.
Basically, the AppDomain class exposes a AssemblyResolve event that can be used to manually load an assembly that fails to load because it wasn't in the normal assembly search chain. You can use then return the corrected path to where the assembly should be found. It's not much code at all.
You can play around with the example found here[^]. And there's a C# example here[^] in answer #2. It's badly formatted, but is easily cleaned up and translates directly to VB.NET.
|
|
|
|
|