-
[RESOLVED] How to put app icon in the TaskBar when the MyApplication_Startup event fires
I have a Splash form that does a few things that takes several seconds to complete.
The Windows TaskBar does not show the app's icon until the Splash form closes and the next form (main form) opens.
How can I get the app's icon to appear in the TaskBar when the app's StartUp event fires (MyApplication_Startup)?
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
That's how most apps work, e.g. I just tested Word, SSMS and VS and they all exhibit the same behaviour. Why should your application be different?
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
jmc -
My app's startup event does several things, including creating folders, verifying the data source, granting Admin read (SELECT) permission on MSysObjects in the underlying MS Access database, loading tables, etc.. This takes several seconds.
When the user double-clicks the app's icon to start the app, the first thing that happens is the app's MyApplication_Startup fires, followed by loading the app's splash form, and when the splash form closes, then the app's main form loads.
So there's a number of things that happen even before the splash form opens and while these things are happening there is no visible indication that the app is starting up (e.g., the app's icon does not appear in the TaskBar until the splash screen loads).
I would like to have the app's icon appear in the TaskBar when the app's MyApplication_Startup event fires.
Is this possible?
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Perhaps you should have provided all the relevant information in the first place. Read your first post and ask yourself whether there's any indication there of a delay BEFORE the splash screen is displayed.
As for the issue, are you using the splash screen functionality built into the VB application framework? It would appear not because I just tried this code:
vb.net Code:
Imports System.Threading
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_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
Thread.Sleep(10000)
End Sub
End Class
End Namespace
and my splash screen displayed pretty much immediately and was visible the whole time that execution was blocked in that Startup event handler. How EXACTLY are you displaying a splash screen? I selected a form as the splash screen on the Application page of the project properties. Did you do the same?
-
3 Attachment(s)
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
jmc -
Thanks for taking the time to look at my question. Your input is most appreciated. I apologize for any vagueness in my first post.
As I mentioned previously, when my app starts I use the Application_Startup event (My.Namespace) to do a few things (setting DPI-Awareness, checking for the database file, verifying the password). At the end of that event, the splash form loads. At the end of the splash form's Load event, the app's main form (Navigator) loads.
When the Application_Startup event fires, the splash form is not loaded and while that event is executing, there is no visible clue to the user that the app is running.
As soon as the splash form's Load event starts, then the user sees that form. The form has a ListBox that shows the user the progress of the startup activities. This list only shows the activities that are performed by the splash form (creating folders, loading data, creating relations, etc.).
I have a laptop and a desktop computer and there is a big difference between these two machines when they run my app. Here's a summary of the app startup performance on the desktop PC:
Attachment 176345
Here's a summary of the app startup performance on the laptop PC:
Attachment 176347
The desktop PC completes the app's startup activities in 10.0 seconds, but the laptop PC takes 51.8 seconds.
In the above summaries, you can see that the biggest differences are in line #6 and line #14. These are where the MS Access database is accessed (no pun intended).
I am running "MS Access 2019 MSO (16.0.12624.20442) 32-bit". I suspect that when Access is accessed, it tries to connect to Microsoft to verify that the user has a licensed copy of the program.
In order to verify that, I disconnected the LAN cable from my desktop PC and then started my app. Sure enough, the app's startup was much slower than the previously shown 10.1 seconds.
Attachment 176353
With no Internet connection, the app took 48.9 seconds to complete it's start-up activities on my desktop PC.
But back to my original question, I would like to give the user a visible clue that the app is running *before* the splash form starts to load (while the Application_Startup event is executing). Is there a way to do this?
As I said, your help is much appreciated. Thanks :)
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
You didn't really answer my question:
Quote:
How EXACTLY are you displaying a splash screen?
There is dedicated splash screen functionality built into the VB application framework but it sounds like you're not using it. It sounds like you have selected your splash form as the startup form and then that is explicitly displaying the main form. If so then that is your mistake. You should have selected the main form as the startup form and the splash for as the splash screen. As I showed with my example, if you do that then the splash form will be displayed while code in the Startup event handler is executed.
The issue is threads. The Startup event handler is executed on the UI thread and the startup form is created and displayed on the UI thread too. If your splash form is the startup form, that's a problem. The UI thread can't do two things at once and the main form isn't even created until the Startup event handler completes. The dedicated splash screen functionality built into the application framework actually creates and displays the splash form on a secondary thread. That's how it is able to be displayed and remain responsive while code is executing in the Startup event handler.
That does create another slight problem though. If you want the splash form to display information about the progress of the Startup event, you have to communicate across a thread boundary. It's not a big deal but it just means calling Invoke or BeginInvoke to do anything in the splash form. I prefer to do that within the splash form itself, making things easier for code accessing the splash screen. If you're interested, I'll whip up an example.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Jmc,
Quote:
The dedicated splash screen functionality built into the application framework actually creates and displays the splash form on a secondary thread. That's how it is able to be displayed and remain responsive while code is executing in the Startup event handler.
This is new information for me. Thanks.
I would like to show the user what’s happening during the app startup, from the Application_Startup event to the first appearance of the main form. I also noted in your post #4 that there’s a couple of MyApplication events that would be useful to use (NetworkAvailabilityChanged and StartupNextInstance).
Quote:
If you want the splash form to display information about the progress of the Startup event, you have to communicate across a thread boundary. It's not a big deal but it just means calling Invoke or BeginInvoke to do anything in the splash form. I prefer to do that within the splash form itself, making things easier for code accessing the splash screen. If you're interested, I'll whip up an example.
An example would be really helpful. I have no experience with cross-thread communications.
I mentioned that the app checks to see if the back-end database file is present when it starts up. I do this in the Application_Startup event because if the database file is not present, then I notify the user and exit the app. In this scenario, the splash form never appears, just a MessageBox dialog.
Code:
'...check back-end database file presence
If FileIO.FileSystem.FileExists(My.Settings.myDataSource) = False Then
If IsDebugMode() Then
Using ofd As New OpenFileDialog
With ofd
.Title = "Please select the data file..."
.Filter = "Microsoft Access|*.accdb"
.InitialDirectory = System.AppDomain.CurrentDomain.BaseDirectory
intResult = .ShowDialog()
End With
If intResult = DialogResult.Cancel Or ofd.FileName = "" Then
'...the user can either select a file and then click Cancel, or click Cancel without selecting a file
' (the OpenFileDialog object will not allow the user to click Open without selecting a file)
strMSG = "The application is cannot proceed without a data file (DEBUG mode)." & vbCrLf & vbCrLf &
"Goodbye."
MessageBox.Show(strMSG, My.Application.Info.Title, MessageBoxButtons.OK, MessageBoxIcon.Error)
Environment.Exit(0)
End If
End Using
Else
strMSG = "The application's data file cannot be found, as shown below:" & vbCrLf & vbCrLf &
vbTab & System.IO.Path.GetFileName(My.Settings.myDataSource) & vbCrLf & vbCrLf &
"Please place this file in the application's startup folder, as shown below:" & vbCrLf & vbCrLf &
System.AppDomain.CurrentDomain.BaseDirectory & vbCrLf & vbCrLf &
"Restart the application after the data file has been placed in the application's startup folder." & vbCrLf & vbCrLf &
"Goodbye."
MessageBox.Show(strMSG, My.Application.Info.Title, MessageBoxButtons.OK, MessageBoxIcon.Error)
Environment.Exit(0)
End If
End If
In a similar way, I'm going to take a look at the MyApplication's NetworkAvailabilityChanged and StartupNextInstance events and exit the app if there is no Internet connection or if the app is already running.
Thanks again for your help.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
Originally Posted by
Mark@SF
In a similar way, I'm going to take a look at the MyApplication's NetworkAvailabilityChanged and StartupNextInstance events and exit the app if there is no Internet connection or if the app is already running.
The StartupNextInstance event is only raised if you have explicitly configured your application to be single-instance, which you also do as part of the Application Framework, on the Application page of the project properties. If you do that then there's no need to handle that event because you'll never get more than one instance anyway. The purpose of that event is if you want to do some sort of processing when the user initiates your application again. For example, lets say that you created an MDI application that was the default application for XYZ files. If the user double-clicked an XYZ file in File Explorer then your app would be opened for the first time and receive the file path as a commandline argument. That would raise the Startup event. You would then open your main form and a child form containing the file content. If the user double-clicked on another XYZ file, the StartupNextInstance event would be raised and you'd get the file path as a commandline argument there, allowing you to open another child window for the new file.
I'll create a splash screen example and post it soon.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Also, you should not be calling Environment.Exit. As I said earlier, if you set e.Cancel to True in the Startup event handler then the startup form will not be created and the application will close of its own accord.
-
1 Attachment(s)
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
jmc -
Thanks. I've removed the Environment.Exit call.
After your post #6, I realized that I don't know enough about Invoke/BeginInvoke, working with multiple threads, etc., so I took some time today to look into these concepts. After a few hours of study, I realized that I have just scratched the surface of these topics so I'm far from being proficient yet.
In order to get up-to-speed, I built a *very* simple demonstration app that only has a couple of forms (splash screen, startup, and login). The splash screen has a ListView control and the form's Load event adds items to the control by calling the AddLiistItem procedure in a For/Next loop.
Code:
For i As Integer = 0 To 9
AddListItem("ListItem #" & i)
System.Threading.Thread.Sleep(1000) '...wait 2 seconds
Next
The AddListItem code is below.
Code:
Public Sub AddListItem(strActivity As String)
'https://www.codeproject.com/Articles/10311/What-s-up-with-BeginInvoke
Dim strMethodName = New System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name '...this procedure's name
Dim flgDebug As Boolean = True '...debug/test purposes only
Try
'If flgDebug Then Debug.WriteLine("{0} ({1})", strMethodName, Date.Now)
If flgDebug Then Debug.WriteLine("{0} ListView has {1} items, adding item: {2}", Now.ToString(“d-MMM-yyyy HH:mm:ss.fff”), lvStatus.Items.Count, strActivity)
If InvokeRequired Then
'...returns true if the thread on which this property is called is not the thread that created this control.
' if InvokeRequired returns true, you need to call either the Invoke or BeginInvoke methods
Invoke(New DelegateToNewListItem(AddressOf AddListItem), strActivity) '...waits till the delegate is executed on the UI thread before returning (synchronous)
'BeginInvoke(New DelegateToNewListItem(AddressOf AddListItem), strActivity) '...returns immediately without waiting for the message to be processed (asynchronous)
Else
Me.NewListItem(strActivity)
End If
Catch ex As Exception
HandleError(ex, True)
Finally
End Try
End Sub
The AddListItem procedure then calls the NewListItem procedure (when InvokeRequired is false). The NewListItem actually adds new items to the ListView, as shown below.
Code:
lvi = New ListViewItem
With lvi
'...the list as a default subitem (index = 0)
.SubItems(0).Text = Date.Now.ToShortDateString
.SubItems.Add(Now.ToString(“HH:mm:ss.fff”))
.SubItems.Add(timerStartup.ElapsedMilliseconds)
.SubItems.Add(strActivity)
End With
lvStatus.Items.Add(lvi)
lvStatus.EnsureVisible(lvStatus.Items.Count - 1)
The Application_Startup event calls the splash screen form's AddListItem procedure and passes it a string (which should be the first item in the ListView control).
Code:
CType(My.Application.SplashScreen, frmSplash).AddListItem("MyApplication_Startup event fired.")
And finally, the splash screen form declares a delegate for the AddListItem procedure:
Code:
Delegate Sub DelegateToNewListItem(strActivity As String)
This simple demonstration app compiles and runs w/out errors. When I single-step through the code while debugging, I can see that the Application_Startup event is successfully calling the splash screen form's AddListItem procedure. The thing is, the item that it adds is placed as the last item in the ListView control when it should be the first item in the list (index = 0).
Attachment 176391
I can see that the item that is added by the Application_Startup event is indeed added as the last item in the ListView control also as the code has Debug.WriteLine statements everytime the AddListItem fires and the NewListItem fires.
From what I've outlined here, can you give me any advice for how to troubleshoot this?
I can upload this simple demonstration project if you'd like to see more of the details.
Thanks for your help. :)
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
Originally Posted by
Mark@SF
The thing is, the item that it adds is placed as the last item in the ListView control when it should be the first item in the list (index = 0).
Whatever it is that you may want to happen, your code does exactly what you see happening:
https://docs.microsoft.com/en-au/dot...tframework-4.8
Quote:
If the ListView.Sorting property is set to a value other than SortOrder.None or if the ListViewItemSorter property is set, the list is sorted after the item is added. Otherwise, the item is inserted at the end of the list. If the list is not sorted, you can use the Insert method to insert an item into the ListView at a specific position.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
I'll take a look at the Sorting property of the ListView. What I really want is to have the items appear in the chronological order of execution (if this was a "real" app, not a demonstration app).
I know that this is multi-threaded issue and as such the timing of the Application_Startup event is variable vs. the timing of the splash screen form's Load event, but I thought that the Application_Startup event would be the first call to the splash screen form's AddListItem procedure (or if not, then at least the 2nd or 3rd item in the list - but not the last item).
Give me a bit and I'll be back with more after I've investigated the Sorting property use.
Thanks.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
Originally Posted by
Mark@SF
I know that this is multi-threaded issue, but I thought that the Application_Startup event would be the first call to the splash screen form's AddListItem procedure (or if not, then at least the 2nd or 3rd item in the list - but not the last item).
I think that I may have misunderstood what you were saying. I thought you meant that you expected Add to place an item at the top of the list but it now appears that you were saying that you expected a particular item to be the first one added and it wasn't. In that case, disregard my previous comment. I'll have a closer look later.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Right now, I'm just trying to understand the interplay between the two threads.
I forgot to mention that at the end of the splash screen form's Load event, the items/subitems of the ListView are written to a .CSV file. When I look at that file, the item from the Application_Startup event is not in the file (even though the Debug.WriteLine statements in the AddListItem and NewListItem procedures indicated that it's item was added to the ListView).
So, there's a lot more here that I need to study. :)
If you want to take a look at my simple app, I'll try uploading a .ZIP file.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Reading post #10 again, it seems like your expectations may be false. You seem to be suggesting that an item added from the Startup event handler should appear in the ListView before items added in the splash screen's own Load event handler. I don't see that that is the case at all. If the splash screen can be displayed while the Startup event handler is doing work then it must be initiated before that event handler is invoked. When the application framework is enable, the system generates a Main method and what I would assume occurs in that method is that a thread is spawned to create and display the splash screen and the Startup event is then raised. If you can access the splash screen object in order to calla method to add a list item then you know that the creation, at least, has occurred. If the secondary thread has got as far as creating the splash screen, there's no reason to think that it hasn't got as far as displaying it, i.e. calling Show. If Show has been called then the Load event handler has likely be invoked and your call from the Startup event handler to add a list item will have to wait until that has completed.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
If Show has been called then the Load event handler has likely be invoked and your call from the Startup event handler to add a list item will have to wait until that has completed.
How would I examine the timing of the calls from the two thread? I'd like to see more of the details as the app executes.
Another question: My splash screen form's ListView has a vertical/horzontal scroll bar. When the form's Load event is executing and items are being added to the list, I'd like to be able to scroll up/down the list to see items that are no longer visible. My code calls the control's EnsureVisible method to make the last item in the list visible, but that then makes the first items not visible. While the list is being populated, the control is not responsive (I can't use the slider on the vertical or horizonal scroll bars).
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
If Show has been called then the Load event handler has likely be invoked and your call from the Startup event handler to add a list item will have to wait until that has completed.
How would I examine the timing of the calls from the two thread? I'd like to see more of the details as the app executes.
Another question: My splash screen form's ListView has a vertical/horzontal scroll bar. When the form's Load event is executing and items are being added to the list, I'd like to be able to scroll up/down the list to see items that are no longer visible. My code calls the control's EnsureVisible method to make the last item in the list visible, but that then makes the first items not visible. While the list is being populated, the control is not responsive (I can't use the slider on the vertical or horizonal scroll bars).
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
jmc -
Sorry for my double reply, the forums site is tricky when posting replies.
You mentioned in your post #6:
Quote:
...and the main form isn't even created until the Startup event handler completes.
I can see this in my code while single-stepping with the debugger. The splash screen form has only added 3 of the 11 items to the ListView when the startup form's Load event fires. (I thought the startup form's Load event was called when the splash screen form closed.)
I don't like it that both forms are visible at the same time (splash screen and startup forms). I can add a call to the startup form's Hide method at the beginning of the form's Load event and also call the startup form's Show method in the FormClosing event of the splash screen form. Is this the preferred technique, or is there something else that I'm not understanding?
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
Originally Posted by
Mark@SF
I thought the startup form's Load event was called when the splash screen form closed.
That would defeat the entire purpose of a splash screen. The reason that splash screens exist is to give the user something to look at while the startup form is prepared. The fact that people chose to use them even when that wasn't necessary, because the startup form took no time to prepare, doesn't change that. What you probably ought to be doing is just checking that the application can start in the Startup event handler and then, once you know that, actually doing the startup work in the Load event handler of your startup form.
Quote:
Originally Posted by
Mark@SF
I don't like it that both forms are visible at the same time (splash screen and startup forms).
They aren't. If they are in your case then it's because you're doing it wrong. It's built into the application framework so you don't have to do the work. You are doing work you don't have to and stepping on the toes of the application framework and breaking something that works in the process.
-
1 Attachment(s)
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
jmc -
Making progress...
The issue with the out-of-sequence items in the ListView control was because there are two threads writing to the control and the timing of these threads is not synchronized (they run asynchronously). So now I am only adding items to the ListView control via the Application_Startup event using calls to the splash screen form's AddNewItem procedure (the splash screen form's Load event does not call the form's AddNewItem procedure).
Attachment 176395
I'm assuming that the startup form's Load event gets implicitly called by the Application_Startup event (at the start? or at the end? or by some other mechanism?).
The startup form's Load event fires immediately when the app starts but does not become visible until the last statement in it's Load event has executed.
In the startup form's Load event, I now call a procedure to write the contents of the splash screen form's ListView control to a .CSV file (see example above) and then close the file. This call to the splash screen form's CloseLog procedure is the last statement in the startup form's Load event and at this point, the splash screen form is still visible but the startup form is not visible..
It seems that when the startup form's Load event finishes, the splash screen form is closed (but it's FormClosing event doesn't hit a breakpoint). So how the form is closed is still a mystery to me (it must be handled implicitly). Is this something you could explain?
Thanks again (and again) for all your help! I've made some solid progress on my journey to master this code. :)
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
Originally Posted by
Mark@SF
I'm assuming that the startup form's Load event gets implicitly called by the Application_Startup event (at the start? or at the end? or by some other mechanism?).
The startup form's Load event handler is no different to the Load event handler of any other form. It gets executed when the Load event is raised, which happens when you call Show or ShowDialog on the form object. The startup form object is not created until the Startup event handler has already completed. That form object is never created if you set e.Cancel to True in the Startup event handler.
Quote:
Originally Posted by
Mark@SF
The startup form's Load event fires immediately when the app starts but does not become visible until the last statement in it's Load event has executed.
Nope. Put a breakpoint on the End Sub line of the Startup event handler and the declaration of the Load event handler and you'll see that Startup ends before Load begins.
All applications start with a Main method. When the application framework is enabled in a VB app, which it is by default, the Main method is hidden from you but it is still there. That's where all this stuff is orchestrated. Take a look at this thread for a C# example I wrote that displays a splash screen on a secondary thread, something like the way VB does it. The Main method generated by the VB application framework would follow a similar pattern but ore like this:
csharp Code:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen.DisplaySplashScreen(mainWindow, 3000);
// Raise Startup event here.
if (<startup was not cancelled>)
{
var mainWindow = new MainWindow();
Application.Run(mainWindow);
}
}
It is inside the Run method that the startup form is displayed and its Load event raised.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
jmc -
Thanks for the clarification re: how the startup form is handled. I've never used the Main method, so that's good to know.
What about the splash screen form? How is it closed...
Quote:
It seems that when the startup form's Load event finishes, the splash screen form is closed (but it's FormClosing event doesn't hit a breakpoint). So how the form is closed is still a mystery to me (it must be handled implicitly). Is this something you could explain?
You've been incredibly patient with all my questions and your comments have really been helpful. Thanks again.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
Quote:
Originally Posted by
Mark@SF
What about the splash screen form? How is it closed...
The MyApplication class in which you're handling the Startup event inherits WindowsFormsApplicationBase. That class has ShowSplashScreen and HideSplashScreen methods. The former spawns a thread on which the splash screen is created and displayed and the latter marshals a call to that thread to close it. The FormClosing and FormClosed events are never raised so I would assume that the Close method is not called. The Dispose method is executed though, so I would assume that HideSpashScreen actually calls Dispose directly. The default minimum time for it to be displayed is 2000 milliseconds and you can change that, so there must be some functionality somewhere that calls HideSplashScreen when that time has expired and the startup form's Load event has completed. Not sure of the specifics though.
-
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
jmc -
I'm amazed at how much you know! Thanks for everything that you've done to help me with these questions. I've learned an a lot because of you. I'm going to mark this thread "resolved".
All the best to you...
-
2 Attachment(s)
Re: How to put app icon in the TaskBar when the MyApplication_Startup event fires
For anyone who might be interested in this topic, a diagram of the application Startup event handler, Splash Screen form, and Startup form interactions is below.
Attachment 176407
The Splash Screen form of the simple demonstration application that I put together to help me learn how to properly use these VB.Net elements is shown below.
Attachment 176409
Thanks again to jmc for his helpful guidance.