How to run a console application without showing the console window - vb.net

I have written an application with the following sub main:
Public Sub Main()
Dim Value As String() = Environment.GetCommandLineArgs
Dim F As Form
Select Case Value.Last.ToLower
Case "-character"
F = New frmCharacterSheet
Case "-viewer"
F = New frmClient
Case Else
F = New frmCombat
End Select
Application.Run(F)
End Sub
This is because I want to be able to install my app with three different startup modes based on the command line. I did have a form that did this, but this has made error trapping very hard because the main form just reports the error.
This console seems to work well but I don't want the user to see the black console screen at startup.
I have searched for the answer but most solutions are 'switch back to a windows forms application'. I don't want to do this though for the above reason. (I cannot use application.run(f) in a winforms start situation because I get a threading error.
I need to know either how to hide the console window, or alternatively how to code a main menu that will launch one of the other three forms (but making them the startup form).
Any help would be appreciated....

Try:
Private Declare Auto Function ShowWindow Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As Boolean
Private Declare Auto Function GetConsoleWindow Lib "kernel32.dll" () As IntPtr
Private Const SW_HIDE As Integer = 0
Sub Main()
Dim hWndConsole As IntPtr
hWndConsole = GetConsoleWindow()
ShowWindow(hWndConsole, SW_HIDE)
'continue your code
End Sub
It has a side effect that the window will be shown and then immediately hidden
valter

"or alternatively how to code a main menu that will launch one of the other three forms (but making them the startup form)."
Start with a standard WinForms Project and use the Application.Startup() event. From there you can check your startup parameters and then dynamically change the Startup form by assigning your desired instance to "My.Application.MainForm". This will cause that form to load as if it was the one originally assigned to the "Startup Form" entry.
Click on Project --> Properties --> Application Tab --> "View Application Events" Button (bottom right; scroll down).
Change the Left dropdown from "(General)" to "(MyApplication Events)".
Change the Right dropdown from "Declarations" to "Startup".
Simplified code:
Namespace My
' The following events are available for MyApplication:
'
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
If True Then
My.Application.MainForm = New Form1 ' <-- pass your desired instance to MainForm
End If
End Sub
End Class
End Namespace

Just go to Project Properties> Application> Application Type> and select Windows Forms Application
At this point your ConsoleApplication turns totally invisible, with no User-Interface.

I just want to add another solution although Idle_Mind has already provided an excellent one. This demonstrates that you can use Application.Run(Form) inside a WinForms app.
Public Class Form1
Private Shared applicationThread As New Threading.Thread(AddressOf Main)
Private Shared Sub Main()
Dim myForm As Form
Dim config = 2 ' if 3, will run Form3
Select Case config
Case 2
myForm = New Form2
Case 3
myForm = New Form3
Case Else
MessageBox.Show("Bad config!")
Exit Sub
End Select
Application.Run(myForm)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
applicationThread.Start()
' immediately dispose Form1 so it's not even shown
Dispose()
End Sub
End Class

Related

Wait for Single Instance occurrences to complete before proceeding to next line VB.NET

Currently, I'm using the 'make app single instance'(MyApplication_StartupNextInstance event) in VB (.net framework win forms) to pass command line arguments from multiple instances to the main form. I'm adding this to a list of string and then passing this list to the next function/ sub. The list captures all the arguments if I add a message box just before calling the next function but then when there's no msgbox, not all the arguments are captured.
I've tired using timers/delays which is a hit and a miss. Tried using timed msgbox that disapper after couple secs, which is the same.
How can I make it wait till all the instances have run and then proceed to the next line of code?
'ApplicationEvents.vb
Private Sub MyApplication_StartupNextInstance(sender As Object, e As ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Dim f = Application.MainForm
If f.GetType Is GetType(my_app_name) Then
CType(f, my_app_name).NewArgumentsReceived(e.CommandLine(0))
End If
End Sub
'my app has the below codes
Public Sub NewArgumentsReceived(args As String)
mylist.Add(args)
End Sub
Private Sub SomeForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
mylist.Add(arg) 'arg is for main form 'args' is for instances
'this is where I want to wait until all the other instances have completed
Anotherfunction(mylist)
End Sub
As I mentioned in my comments, the StartupNextInstance event can be raised any time so you should design your app to react to it at any time. The initial instance has no idea how many subsequent instances there will be or when they will start so it should simply react to them one at a time, whenever they occur. Here's an example of a single instance application where the main form is an MDI parent and the commandline arguments are text file paths that each get opened in a child window.
Child form:
Imports System.IO
Public Class ChildForm
Public Property FilePath As String
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.Text = File.ReadAllText(FilePath)
End Sub
End Class
Parent form:
Public Class ParentForm
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim args = Environment.GetCommandLineArgs()
If args.Length > 1 Then
LoadChildForm(args(1))
End If
End Sub
Public Sub LoadChildForm(filePath As String)
Dim child As New ChildForm With {.MdiParent = Me,
.FilePath = filePath}
child.Show()
End Sub
End Class
Application events:
Imports Microsoft.VisualBasic.ApplicationServices
Namespace My
' The following events are available for MyApplication:
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_StartupNextInstance(sender As Object, e As StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Dim args = e.CommandLine
If args.Count > 0 Then
DirectCast(MainForm, ParentForm).LoadChildForm(args(0))
End If
End Sub
End Class
End Namespace
In this case, the initial instance doesn't wait for anything. It just goes ahead and does what it does with its own commandline argument. It has no idea if there will be any subsequent instances or, if there are, how many there will be and when they will start, so it would have no idea what it was waiting for. Any time another instance is started, the initial instance reacts then, using the commandline argument provided.

vb.net Parameters Startup Form not hiding?

I am creating a application to be used with a touch panel device. The touch panel device comes with a standard windows OSK (On screen Keyboard). Whilst testing its been concluded that the standard OSK is to large and too complex for what we need it in. So I have built my own OSK. some of the feilds though only requier numeric inputs so I though of futher simplifying the process by creating a new form which hosts a numeric pad. so far this is all working. the idea is then to have the app which ask for diffrent inputs then to trigger the OSK application, say that the user wants to enter a phonenumber in one textbox I then want to start the OSK app using a parameter that trigers the OSK to start the NumericForm form first... this too I have working but the thing I can't get right is to hide the AlphabetForm I have tried the following method but am a little stumpt on how to get this right
In short its the Me.hide which isnt working as expected?
Private Sub AlphabetForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
#Region "Recive startup parameters (if any)"
Try
Dim OSKParameters As String = Command()
If OSKParameters = "OSKNUM" Then
NumericForm.Show()
Me.Hide()
Else
ShiftSelect = 0
End If
Catch ex As Exception
'Do nothing
End Try
#End Region
End Sub
Three possible setups, as described in comments:
► Using the Application Framework, override OnStartup and set Application.MainForm to a Form object determined by a command-line argument:
To generate ApplicationEvents.vb, where Partial Friend Class MyApplication is found, open the Project properties, Application pane, click the View Application Events Button: it will add the ApplicationEvents.vb file to the Project if it's not already there.
Imports Microsoft.VisualBasic.ApplicationServices
Partial Friend Class MyApplication
'[...]
Protected Overrides Function OnStartup(e As StartupEventArgs) As Boolean
Application.MainForm = If(e.CommandLine.Any(
Function(cmd) cmd.Equals("OSKNUM")), CType(NumericForm, Form), AlphabetForm)
Return MyBase.OnStartup(e)
End Function
'[...]
End Class
► Disabling Application Framework, to start the Application from Sub Main().
In the Project->Properties->Application pane, deselect Enable application framework and select Sub Main() from the Startup form dropdown.
If Sub Main() doesn't exist yet, it can be added to a Module file. Here, the Module is named Program.vb.
Module Program
Public Sub Main(args As String())
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(True)
Dim startForm as Form = If(args.Any(
Function(arg) arg.Equals("OSKNUM")), CType(New NumericForm(), Form), New AlphabetForm())
Application.Run(startForm)
End Sub
End Module
► If the OSK can be moved to UserControls, similar to what jmcilhinney suggested, run the default container Form and select the UserControl to show using the same logic (inspecting the command-line arguments):
Public Class AlphabetForm
Public Sub New()
InitializeComponent()
Dim args = My.Application.CommandLineArgs
Dim uc = If(args.Any(
Function(arg) arg.Equals("OSKNUM")), CType(New NumericUC(), UserControl), New AlphabetUC())
Me.Controls.Add(uc)
uc.BringToFront()
uc.Dock = DockStyle.Fill
End Sub
End Class

Events raised from a ShowDialog Form aren't raised all the way to a calling vb6 app via com interop?

We have a legacy VB6 app where we have been calling .net assemblies for a long time, including displaying WinForms from the .net assembly. But now I also need to raise events from the .net assembly, from the WinForm, back up to the VB6 app.
This works (the VB6 event fires) when the form is displayed with .Show. But when the form is displayed with .ShowDialog, the event doesn't fire in the VB6 app. And, of course, I need to show the form(s) modally, so that's why .ShowDialog is being used.
Code:
Create a .net class library, enabling com interop. This is named ClassLibrary2 when I created it on my machine.
Option Strict On
Option Explicit On
<ComClass(Class1.ClassId, Class1.InterfaceId, Class1.EventsId)>
Public Class Class1
#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "3E245773-5A31-4B09-A26B-19D2E593395E"
Public Const InterfaceId As String = "5A184A72-AE12-4564-83FB-15EEAC8C9A13"
Public Const EventsId As String = "75C80E42-6B66-4B43-A1FA-BD62C95D117E"
#End Region
Public Sub New()
MyBase.New
End Sub
Public Event MyEvent(sParm As String)
Private WithEvents ofrm As Form1
Public Sub MySub()
RaiseEvent MyEvent("MySub Entry")
ofrm = New Form1
'ofrm.Show() ' With .Show, all events are raised to calling app
ofrm.ShowDialog() ' With .ShowDialog, events from the form are raised to this class, but then subsequently aren't raised to the calling app
RaiseEvent MyEvent("MySub Exit")
End Sub
Private Sub ofrm_MyFormEvent(sParm As String) Handles ofrm.MyFormEvent
RaiseEvent MyEvent(sParm)
End Sub
End Class
Form added to the assembly, then a button added to the form.
Option Strict On
Option Explicit On
Public Class Form1
Public Event MyFormEvent(sParm As String)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RaiseEvent MyFormEvent("Form Closing")
Me.Close()
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
RaiseEvent MyFormEvent("Form Shown")
End Sub
End Class
VB6 executable application, with a button on the Form, also a reference added to the ClassLibrary2 assembly.
Private WithEvents ox As ClassLibrary2.Class1
Private Sub Command1_Click()
Set ox = New ClassLibrary2.Class1
Call ox.MySub
End Sub
Private Sub ox_MyEvent(ByVal sParm As String)
Debug.Print sParm
End Sub
When running all this, the events raised from MySub fire the VB6 ox_MyEvent event handler. And when Form1 is displayed with .Show, the events raised from the form fire the ofrm_MyFormEvent handler, which raises the event further, and the VB6 ox_MyEvent event handler fires:
MySub Entry
MySub Exit
Form Shown
Form Closing
But when Form1 is displayed with .ShowDialog, the events raised from the form fires the ofrm_MyFormEvent handler, but the event raised from there never fires the VB6 ox_MyEvent event handler:
MySub Entry
MySub Exit
What's happening here? Some kind of VB6 UI thread blocking? But why would the ShowDialog be doing that when the events raised from MySub can get through?
Using VS2015, Framework 4.5.2
Update
I've run another test, using a .net Winform app (exe) as the calling application, instead of the VB6 code above. Tried calling the ClassLibrary2 assembly as a .net assembly, and also as a COM object. In both cases, the events fired up to the main app as expected:
MySub Entry
Form Shown
Form Closing
MySub Exit
So it's not a COM issue (or at least not just COM), definitely involving something with VB6?
Modal dialogs have their own inner message pumps. Perhaps because that inner message pump is .NET controlled instead of VB6 controlled, there is an incompatibility? As in, the VB6 message loop is satisfying something VB6 needs that the .NET message pump is not doing?
In any case, personally I moved away from events in COM interop scenarios in favor of using an explicit callback interface. The advantage of this is there is no message pump involved at all, you bypass any issues related to that.
So instead of Public Event MyEvent(sParm As String) create an Interface call IClass1EventListener or IClass1CallbackHandler and give it a method Sub OnMyFormEvent(sParm As String)
Then in your VB6 Form use Implements IClass1EventListener.
Your code then looks more like this:
Private ox As ClassLibrary2.Class1
Private Sub Command1_Click()
Set ox = New ClassLibrary2.Class1
Call ox.MySub(Me)
End Sub
Private Sub IClass1EventListener_OnMyEvent(ByVal sParm As String)
Debug.Print sParm
End Sub
You should take care that to either have the .NET side explicitly clear the callback reference after it completes the ShowDialog or change the method signature to one that your VB6 code can manage it, like this:
Private Sub Command1_Click()
Set ox = New ClassLibrary2.Class1
Set ox.Listener = Me
Call ox.MySub()
Set ox.Listener = Nothing
End Sub

how to pass commandlinearguments from to a Running Application [duplicate]

I have already implemented context menu to appear when a user right-clicks a file in windows explorer using Registry. The file address will be passed to the application as command lines. Parsing it is no problem.
How can I implement that is similar to "Add To Windows Media Player Playlist"? It does not open another instance of the app but works on the same open window and adds it to a list?
There are 2 ways to do this depending on how your app starts.
Method 1: Using VB App Framework and a MainForm
This is the easiest because you mainly just need to add some code for an Application event. First, add a method to your main form to receive new arguments from subsequent instances of your app:
Public Class MyMainForm ' note the class name of the form
...
Public Sub NewArgumentsReceived(args As String())
' e.g. add them to a list box
If args.Length > 0 Then
lbArgs.Items.AddRange(args)
End If
End Sub
Next:
Open Project Properties
Check the "Make Single Instance" option
At the bottom, click View Application Events
This will open a new code window like any other; Select MyApplication Events in the left drop down; and StartupNextInstance in the right one.
Here, we find the main form and send the command line arguments to the method we created:
Private Sub MyApplication_StartupNextInstance(sender As Object,
e As ApplicationServices.StartupNextInstanceEventArgs) _
Handles Me.StartupNextInstance
Dim f = Application.MainForm
' use YOUR actual form class name:
If f.GetType Is GetType(MyMainForm) Then
CType(f, MyMainForm).NewArgumentsReceived(e.CommandLine.ToArray)
End If
End Sub
Note: Do not try to fish the main form out of Application.OpenForms. A few times I've had it fail to find an open form in the collection, so I have quit relying on it. Application.MainForm is also simpler.
That's it - when a new instance runs, its command line args should be passed to the form and displayed in the listbox (or processed however your method sees fit).
Method 2: Starting From Sub Main
This is more complicated because starting your app from a Sub Main means that the VB Application Framework is not used, which provides the StartupNextInstance event. The solution is to subclass WindowsFormsApplicationBase to provide the functionality needed.
First, give your main form a meaningful name and add something like the NewArgumentsReceived(args As String()) as above.
For those not aware, here is how to start your app from Sub Main():
Add a module named 'Program' to your app
Add a Public Sub Main() to it.
Go to Project -> Properties -> Application
Uncheck Enable Application Framework
Select your new "Sub Main" as the Startup Object
The module can actually be named anything, Program is the convention VS uses for C# apps. The code for Sub Main will be later after we create the class. Much of the following originated from an old MSDN article or blog or something.
Imports Microsoft.VisualBasic.ApplicationServices
Imports System.Collections.ObjectModel
Public Class SingleInstanceApp
' this is My.Application
Inherits WindowsFormsApplicationBase
Public Sub New(mode As AuthenticationMode)
MyBase.New(mode)
InitializeApp()
End Sub
Public Sub New()
InitializeApp()
End Sub
' standard startup procedures we want to implement
Protected Overridable Sub InitializeApp()
Me.IsSingleInstance = True
Me.EnableVisualStyles = True
End Sub
' ie Application.Run(frm):
Public Overloads Sub Run(frm As Form)
' set mainform to be used as message pump
Me.MainForm = frm
' pass the commandline
Me.Run(Me.CommandLineArgs)
End Sub
Private Overloads Sub Run(args As ReadOnlyCollection(Of String))
' convert RO collection to simple array
' these will be handled by Sub Main for the First instance
' and in the StartupNextInstance handler for the others
Me.Run(myArgs.ToArray)
End Sub
' optional: save settings on exit
Protected Overrides Sub OnShutdown()
If My.Settings.Properties.Count > 0 Then
My.Settings.Save()
End If
MyBase.OnShutdown()
End Sub
End Class
Note that the three main things the App Framework can do for us ("Enable XP Styles", "Make Single Instance" and "Save Settings on Exit") are all accounted for. Now, some modifications to Sub Main:
Imports Microsoft.VisualBasic.ApplicationServices
Imports System.Collections.ObjectModel
Module Program
' this app's main form
Friend myForm As MyMainForm
Public Sub Main(args As String())
' create app object hardwired to SingleInstance
Dim app As New SingleInstanceApp()
' add a handler for when a second instance tries to start
' (magic happens there)
AddHandler app.StartupNextInstance, AddressOf StartupNextInstance
myForm = New MyMainForm
' process command line args here for the first instance
' calling the method you added to the form:
myForm.NewArgumentsReceived(args)
' start app
app.Run(myForm)
End Sub
' This is invoked when subsequent instances try to start.
' grab and process their command line
Private Sub StartupNextInstance(sender As Object,
e As StartupNextInstanceEventArgs)
' ToDo: Process the command line provided in e.CommandLine.
myForm.NewArgumentsReceived(e.CommandLine.ToArray)
End Sub
End Module
The SingleInstanceApp class can be reused with any Sub Main style app, and the code in that method is mainly a copy-paste boilerplate affair except for perhaps the form reference and actual name of the NewArgumentsReceived method.
Testing
Compile the app, then using a command window, send some commandline arguments to the app. I used:
C:\Temp>singleinstance "First Inst" apple bats cats
This starts the app as normal, with the arguments shown. Then:
C:\Temp>singleinstance "Next Inst" ziggy zoey zacky
C:\Temp>singleinstance "Last Inst" 111 222 3333
It doesnt matter which approach you use - they both work the same. The Result:
Note that depending on security settings, your firewall may request permission for apps using either method to connect to other computers. This is a result of how an instance sends or listens for the arguments from others. At least with mine, I can deny permission to connect and everything still works fine.
#Plutonix solution is quite efficient and elegant.
However if you program goes through multiple Forms i.e. if the Main Form can change during program execution, for example if you have a login form and then a main form, or a sequence of non-modal forms, Application.MainForm won't always be the same form and may not be known beforehand (hard coded).
Plutonix code assumes it is known and hard codes it.
In this case, you may want to be able to receive the NewArguments at all times, in whichever form is active at the time in your application.
There are 2 solutions to extend Plutonix solution:
1) Repeatedly force Application.MainForm to a specific form in code (I haven't tested this but Application.MainForm is Read/Write so it could work).
2) The most elegant is to implement an Interface on all forms that can possibly become the MainForm:
Create the Basic interface:
Public Interface INewArgumentsReceived
Sub NewArgumentsReceived(args As String())
End Interface
Modify #Plutonix code for MyApplication_StartupNextInstance to:
Private Sub MyApplication_StartupNextInstance(sender As Object, e As ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Dim f = Application.MainForm
If f.GetType.GetInterfaces.Contains(GetType(INewArgumentsReceived)) Then
CType(f, INewArgumentsReceived).NewArgumentsReceived(e.CommandLine.ToArray)
Else
MsgBox("The current program state can't receive new requests.",, vbExclamation)
End If
Now on all possible forms that can become the Main Form, implement the INewArgumentsReceived Interface:
Public Class FormA: Implements INewArgumentsReceived
Public Sub NewArgumentsReceived(args As String()) Implements INewArgumentsReceived.NewArgumentsReceived
MsgBox("Got new arguments")
End Sub
The other advantage of using the Interfaces is that we can check if the current Application.MainForm implements it and is able to receive it.
If the current Application.MainForm does not implement the Interface it fails gracefully with an informational message.

How to Open console in VB

I currently have a console application by using the setting illustrated in the image bellow. However Now I wish to open multiple forms with the console so I'm wondering if I can somehow open multiple forms or open the console within a Windows Forms Application
#tinstaafl can you share this extra programming or a link to a
solution. Thanks
Here's a couple of links:
Console and WinForm together for easy debugging
Console Enhancements
Here's a conversion of the first one. You'll need a form with a checkbox name "CheckBox1":
Imports System.Runtime.InteropServices
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
If CheckBox1.Checked Then
Win32.AllocConsole()
Console.WriteLine("Done!")
Else
Win32.FreeConsole()
End If
End Sub
End Class
Public Class Win32
<DllImport("kernel32.dll")> Public Shared Function AllocConsole() As Boolean
End Function
<DllImport("kernel32.dll")> Public Shared Function FreeConsole() As Boolean
End Function
End Class
Everytime you click the checkbox you show or hide the console. You can write to and read from the same as any console app.
Forms and Console applications are very different. So much so that generally speaking a process either needs to be a form or console application. Forms applications are implemented with a message pump and console applications are command line drive. It is possible to a degree to run a form within a console, and vice versa, but generally not recommended. If you truly need both I would highly encourage you to use 2 processes.
If you could elaborate a bit more on your use case we may be better able to help you out.
So this is very cool. In the designer just add a checkbox using the Toolbox common controls.
Then double click on the new "CheckBox1" and that will automatically insert this sub routine:
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
End Sub
Then all you have to do is add this code:
If CheckBox1.Checked Then
Win32.AllocConsole()
Console.WriteLine("Done!")
Else
Win32.FreeConsole()
End If
When you run your windows form program and check the box it will automatically open the window and KEEP it open until you uncheck the box.
Add this class to the bottom of your program:
Public Class Win32
<DllImport("kernel32.dll")> Public Shared Function AllocConsole() As Boolean
End Function
<DllImport("kernel32.dll")> Public Shared Function FreeConsole() As Boolean
End Function
End Class
And be sure to add the Imports statement at the top
Imports System.Runtime.InteropServices
If you want to open a console window to interact with and when you close the console, that action won't terminate your windows program then you can add these two lines of code:
Dim myProcess As Process
myProcess = Process.Start("cmd.exe")