I have the below code to check if 'chrome' is running when I click Button1. If not it launches chrome. This works but I dont know the code needed in the If statement to switch to the chrome if its already running. Hopefully this is something very simple i am missing.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If Process.GetProcessesByName("chrome").Count > 0 Then
??**SHOW RUNNING APPLICATION**??
Else
Process.Start("C:\Program Files\Google\Chrome\Application\chrome.exe")
End If
End Sub
As I mentioned in my above comment, Chrome starts many instances of itself. Each tab has its own process, so how are you going to tell it which one to switch to?. This come's down to what tab was selected when you minimize the window or it minimizes itself to the task bar. Below should help you out and it's tried and tested. The only issue is, if you open Chrome and have multiple tabs it's fine, but if you create another instance of Chrome it will not show the second instance, it will only bring forward the first instance... If you close the first instance, the second instance of course will come forward.
Public Class Form1
#Region "DLL Imports"
<System.Runtime.InteropServices.DllImport("User32.dll")> _
Private Shared Function SetForegroundWindow(handle As IntPtr) As Boolean
End Function
<System.Runtime.InteropServices.DllImport("User32.dll")> _
Private Shared Function ShowWindow(handle As IntPtr, nCmdShow As Integer) As Boolean
End Function
<System.Runtime.InteropServices.DllImport("User32.dll")> _
Private Shared Function IsIconic(handle As IntPtr) As Boolean
End Function
#End Region
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
StartOrShowProcess("chrome")
End Sub
Private Sub StartOrShowProcess(ByVal strProcessName As String)
Try
Dim handle As IntPtr
Dim proc As Process() = Process.GetProcessesByName(strProcessName)
If proc.Count > 0 Then
For Each procP As Process In proc
handle = procP.MainWindowHandle
If handle <> 0 AndAlso IsIconic(handle) Then 'Do we have a handle and is it minimized?
ShowWindow(handle, 9)
SetForegroundWindow(handle)
End If
Next
Else 'Not running or started...
Process.Start(strProcessName)
End If
Catch ex As Exception
'Handle your error...
End Try
End Sub
End Class
Related
I have been able to run an external program using the following code.
Imports System.Runtime.InteropServices
Public Class Form1
<DllImport("user32.dll")> Public Shared Function SetParent(ByVal hwndChild As IntPtr, ByVal hwndNewParent As IntPtr) As Integer
End Function
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Dim PRO As Process = New Process
PRO.StartInfo.FileName = ("notepad.exe")
PRO.Start()
Do Until PRO.WaitForInputIdle = True
'Nothing
Loop
SetParent(PRO.MainWindowHandle, Me.Handle)
PRO.Dispose()
End Sub
This works fine..... (for notepad that is)
However If I swich notepad for my own vb.net application it fails to launch that aplication inside the form but rather runs it outside of the form. I thought that the application I am trying to launch might of had somthing in it so I created a new application with nothing in it (as bare as I could get it) and run that instead of notepad but it also fails to launch within its "parent" form but rather it also triggers outside of the "parent" form insted?
Could someone please help me fix this?
You just need to wait a tiny bit longer for the MainWindowHandle property to be populated.
Here's a kludge that'll do it:
Private Async Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Dim PRO As Process = New Process
PRO.StartInfo.FileName = ("C:\Users\mikes\Desktop\temp.exe")
PRO.Start()
Await Task.Run(Sub()
PRO.WaitForInputIdle()
While PRO.MainWindowHandle.Equals(IntPtr.Zero)
Threading.Thread.Sleep(10)
End While
End Sub)
SetParent(PRO.MainWindowHandle, Me.Handle)
End Sub
If you want a ten second fail-safe, and exceptions caught, then you could change it up to:
Private Async Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Try
Dim PRO As Process = New Process
PRO.StartInfo.FileName = ("C:\Users\mikes\Desktop\temp.exe")
PRO.Start()
Await Task.Run(Sub()
Dim timeout As DateTime = DateTime.Now.AddSeconds(10)
While timeout > DateTime.Now AndAlso PRO.MainWindowHandle.Equals(IntPtr.Zero)
Threading.Thread.Sleep(10)
End While
End Sub)
If (Not PRO.MainWindowHandle.Equals(IntPtr.Zero)) Then
SetParent(PRO.MainWindowHandle, Me.Handle)
Else
MessageBox.Show("Timed out waiting for main window handle.", "Failed to Launch External Application")
End If
Catch ex As Exception
MessageBox.Show(ex.ToString, "Failed to Launch External Application")
End Try
End Sub
I have a button which on click will run a Sub, creating a process which runs a script.
When this script is finished an Exited handler will fire and run another Sub which cleans up so that the application is ready to go anew without restarting it.
I disable the button during the run and try to re-enable it when the Exit is fired, however it tells me that the button is in another thread. So I tried using SynchronizedContext and Post:
Declared at the start of my class:
Class MainWindow
Private sc As SynchronizationContext = SynchronizationContext.Current
Not sure if I'm doing that correctly but it worked for me elsewhere in the code where I had the same problem.
The exit handling sub:
Private Sub CMD_Exited(ByVal sender As Object, ByVal e As EventArgs)
myProcess.CancelOutputRead()
myProcess.CancelErrorRead()
sc.Post(AddressOf Button_Click, Button1.IsEnabled = True)
Close()
End Sub
Which errors:
Method 'Private Sub Button_Click(sender As Object, e As RoutedEventArgs)' does not have a signature compatible with delegate 'Delegate Sub SendOrPostCallback(state As Object)'.
What can I do here? Changing the button signature will cause incompatibilities elsewhere.
Are there better ways to get around this threads issue?
Visual Vincent is correct, you need to invoke on the UI thread. Specifically you need to read this How to: Make Thread-Safe Calls to Windows Forms Controls.
Public Delegate Sub DoProcessStuffOnUIThreadHandler()
Private Sub CMD_Exited(ByVal sender As Object, ByVal e As EventArgs)
If Me.Button1.InvokeRequired Then
Dim d As New DoProcessStuffOnUIThreadHandler(AddressOf DoProcessStuffOnUIThread)
Me.Button1.Invoke(d)
Else
DoProcessStuffOnUIThread()
End If
End Sub
Private Sub DoProcessStuffOnUIThread()
myProcess.CancelOutputRead()
myProcess.CancelErrorRead()
Button1.IsEnabled = True
Close()
End Sub
(28-SEP-2017) Edit to add an alternative, that I used frequently in my WinForms code days, for brevity:
Public Delegate Sub DoProcessStuffOnUIThreadHandler()
Private Sub CMD_Exited(ByVal sender As Object, ByVal e As EventArgs)
If Me.Button1.InvokeRequired Then
Dim d As New DoProcessStuffOnUIThreadHandler(AddressOf CMD_Exited)
Me.Button1.Invoke(d)
Else
myProcess.CancelOutputRead()
myProcess.CancelErrorRead()
Button1.IsEnabled = True
Close()
End If
End Sub
The added example simply reduces code use. Both examples end in the same result. Hope that helps.
I know this is going to be virtually impossible to answer without me posting code, so I'll try to give some examples to aid this but...
I have a written dll which does some processing. I have it async tasks which report back status messages, such as the thing it's currently working on. There are multiple threads running each processing a different thing.
Now... when I use my DLL in a console app, the status.report("what I'm doing") works fine. I have a method in my console app with a Console.Writeline(text) which works great.
However... when I use the SAME dll in a gui form, and use the SAME methods from the console within the form to run the SAME processes with the SAME data, the SAME method that works perfectly writing the line to the console is NOT triggered and NO report is even processed by the gui.
Example.
console app:
Imports myDLL
Module Module1
Sub Main
SAE(paramaters).wait()
End Sub
Private Async Function SAE(parameters) as Task
Dim progress_indicator As Progress(Of Integer) = New Progress(Of Integer)(AddressOf DisplayProgress)
Dim progress_text As Progress(Of String) = New Progress(Of String)(AddressOf textProgress)
Dim complete As Object = Nothing
complete = Await Task.Run(Function() MyDLL.Process1(other parameters, progress_indicator, progress_text))
End Function
Private Sub DisplayProgress(ByVal percentage As Decimal)
Console.WriteLine("percentage " + Format(percentage, "0.00"))
End Sub
Private Sub textProgress(ByVal text As String)
Console.WriteLine("sub - reporting: " + text)
End Sub
End Module
Public Class myDLL
Public Function SettleAll(other paramaters, progress_indicator As IProgress(Of Integer), status As IProgress(Of String)) As Boolean
Dim aThread As Thread
aThread = New Thread(Sub() _OtherProcess(other parameters, progress_indicator, status))
aThread.Start()
System.Threading.Thread.Sleep(10)
aThread.Join
End Function
Private Sub _OtherProcess(other parameters, progress_indicator, status))
Loop
'Do Some stuff...
status.Report("Report back this it's working on this, that or the other")
progress_indicator.Report(SomePercentageProgressVariable))
End Loop
End Function
End Class
Now... when I use this, I get messages in the console window as I expect. However... in the gui... when I copy the SAE method and put the Sub Main code into a button click like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SAE(paramaters).wait()
End Sub
... and I change the following methods:
Private Sub DisplayProgress(ByVal percentage As Decimal)
Debug.Print("percentage " + Format(percentage, "0.00"))
End Sub
Private Sub textProgress(ByVal text As String)
TextBox1.AppendText(text)
Debug.Print("sub - reporting: " + text)
End Sub
NOTHING at all happens...
The DLL is doing the processing, but there's no reporting.
Think I solved it.
If I change the button on_click method to an Async method llike this:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Await SAE(paramaters)
End Sub
It seems to work
I have a small form that notifies the user on the completion of events (such as SMO restore).
I want this form to appear from various sources (such as the below SMO Restore complete event) so I guess I need to create a new thread before creating the form? As it could be called from outside the UI thread. (I also need to pass a string to this form)
The child form fades in out using a timer + Opacity.
What am I doing wrong here?
Private Sub CompleteEventHandler(ByVal sender As Object, ByVal e As Microsoft.SqlServer.Management.Common.ServerMessageEventArgs)
MyThread = New System.Threading.Thread(AddressOf DoStuff)
MyThread.Start("meh")
End Sub
Private Delegate Sub DoStuffDelegate(ByVal MsgString As String)
Private Sub DoStuff(ByVal MsgString As String)
If Me.InvokeRequired Then
Me.Invoke(New DoStuffDelegate(AddressOf DoStuff))
Else
Dim TempMessage As New frmNotification
TempMessage.lblMessage.Text = MsgString
TempMessage.Show()
End If
End Sub
Don't start a new thread, there's no point since you're already running on another thread and InvokeRequired will always be True. The mistake is that you call Me.Invoke() but forget to pass the "MsgString" argument. You'll also want to use Me.BeginInvoke(), no need to wait. Thus:
Private Sub CompleteEventHandler(ByVal sender As Object, ByVal e As EventArgs)
Me.BeginInvoke(New DoStuffDelegate(AddressOf DoStuff), "meh")
End Sub
Private Sub DoStuff(ByVal MsgString As String)
Dim TempMessage As New frmNotification
TempMessage.lblMessage.Text = MsgString
TempMessage.Show()
End Sub
i've a app that starts from a sub in a module, do a few things, and then load the form.
But it doesn't work :/
Here we execute dBase.AddTemporalFilepath
module.vb
Public dBase As New Core.clsDatabase
Public Sub Main()
FurBase.Directory = My.Application.Info.DirectoryPath
If appMutex.WaitOne(TimeSpan.Zero, True) Then
ShowUploader()
End If
Dim returnValue As String()
returnValue = Environment.GetCommandLineArgs()
If returnValue.Length > 1 Then
If My.Computer.FileSystem.FileExists(returnValue(1).ToString) Then
dBase.AddTemporalFilepath(returnValue(1).ToString)
End If
End If
End Sub
Private Sub ShowUploader()
Application.EnableVisualStyles()
Application.Run(frmUploader)
End Sub
We raise the event TempFilepathAdded
clsDatabase.vb
Public Class clsDatabase
Public Event TempFilepathAdded()
Public Function AddTemporalFilepath(ByVal filepath As String)
...
RaiseEvent TempFilepathAdded()
...
End Function
End Class
We catch the event
form.vb
Private Sub form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler dBase.TempFilepathAdded, AddressOf TempFilepathAddedHandler
End Sub
Private Sub TempFilepathAddedHandler()
MsgBox("Event raised")
End Sub
Any Idea?
More info:
The event is raised when the form is closed.
The line "Application.Run(frmUploader)" pauses your program until the Window closes. Basically it hijacks the main thread to handle stuff like users clicking buttons.
Normally your Main function should look like this:
Setup
Application.Run
Clean-up
Sorry, but it looks like its time to reorganize your code.