Link to home
Start Free TrialLog in
Avatar of PNRT
PNRT

asked on

vb.net programming the Windows7 taskbar icons

Hi experts.  Without using WPF or Windows-API-Code-Pack-1.1, can anyone please advise if it is possible to add right click commands to the VB programs icons  on the taskbar?    Sort of like the system tray but using the taskbar icons?  If so please point me in the right direction for sample code.   The only thing I can find is Windows-API-Code-Pack-1.1  which is unsupported and may not last past Windows 7.  Many Thanks
Avatar of Brendan M
Brendan M
Flag of Australia image

Set objAppl = CreateObject("Shell.Application")

Sub Pin(Path, File)
	If objFSO.FileExists(Path & "\" & File) Then
		Set objFolder = objAppl.Namespace(Path)
		Set objFolderItem = objFolder.ParseName(File)
		Set colVerbs = objFolderItem.Verbs
		For Each objVerb in colVerbs
			If Replace(objVerb.name, "&", "") = "Pin to Taskbar" Then objVerb.DoIt
		Next
	End If
End Sub

Open in new window


vbs code though\
but you may be a starting point
Avatar of PNRT
PNRT

ASKER

Hi - Thanks for the reply.  Apologies and please correct me if I'm wrong but doesn't this just pin to the taskbar?
I was looking for the ability to add commands to the right click function of the icon in the taskbar
ASKER CERTIFIED SOLUTION
Avatar of it_saige
it_saige
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of PNRT

ASKER

Many thanks indeed, this is an excellent response.    Just one thing.  You mentioned the API Code pack.  This looked perfect for what I needed but I was worried about the fact that it was not supported and that it seemed to be very Windows 7 oriented.   What is your view on it?  Would it be OK with Windows 8.1, Is it stable?    I am mainly interested in Windows 7 and Windows 8  

Many Thanks again for your help
I'm not finding anywhere that indicates that the Windows API Pack is not supported.  As for it's availability beyond Windows 7.  Looking at the latest version of Windows (Windows 10), the GUI still supports the same functionality and features as Windows 7.  Also, yes there is the Metro Interface on Windows 8/8.1 but that only accounts for a part of the user experience.  The desktop still uses the same elements introduced in Windows 7.User generated imageUser generated imageThat being said, while you may design this for Windows 7 (and for the time being, 8, 8.1 and 10), this should not preclude you from considering the kernels that were used for Vista, XP, 2000 and 2003.  You can essentially have your cake and eat it too.

Here is one method (using the SendMessage API to send a Registered Message based on the Code Project by Ahmed Said):

1. Using the [url="https://visualstudiogallery.msdn.microsoft.com/27077b70-9dad-4c64-adcf-c7cf6bc9970c

NuGet Package Manager extension[/url] I installed the WindowsAPIPack Core and Extensions."]User generated imageUser generated imageUser generated imageUser generated imageUser generated imageUser generated image

2. Make your project a 'Single Instance' application.

User generated imageUser generated image

3. Add the Native Methods for this example.

Add a class called 'NativeMethods.vb' to your project and place the following into the code file:
Imports System.Runtime.InteropServices

Public Class NativeMethods
	''' Define our Constants we will use
	Public Const WM_SYSCOMMAND As Int32 = &H112
	Public Const MF_SEPARATOR As Int32 = &H800
	Public Const MF_BYPOSITION As Int32 = &H400
	Public Const MF_STRING As Int32 = &H0
	Public Const MF_SETTINGS As Int32 = 1000
	Public Const MF_ABOUT As Int32 = 1001

	<DllImport("user32.dll")> _
	Public Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
	End Function

	<DllImport("user32.dll")> _
	Public Shared Function GetSystemMenu(ByVal hWnd As IntPtr, ByVal bRevert As Boolean) As IntPtr
	End Function

	<DllImport("user32.dll")> _
	Public Shared Function InsertMenu(ByVal hMenu As IntPtr, ByVal wPosition As Int32, ByVal wFlags As Int32, ByVal wIDNewItem As Int32, ByVal lpNewItem As String) As Boolean
	End Function

	<DllImport("user32.dll")> _
	Public Shared Function RegisterWindowMessage(ByVal msgName As String) As Integer
	End Function

	<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
	Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
	End Function
End Class

Open in new window

4. Create the Global settings for this example.

Add a module to your project named 'Globals.vb' and add the following to the code file:
Imports Microsoft.WindowsAPICodePack.Shell
Imports Microsoft.WindowsAPICodePack.Taskbar
Imports System.IO

Module Globals
	Public Property IsUsingStandardAPIMenu() As Boolean = False
	Public Property Windows7APIMenu() As JumpList
	Public ShowAboutArg As Integer = NativeMethods.RegisterWindowMessage("JumpList.ShowAboutArg")
	Public ShowSettingsArg As Integer = NativeMethods.RegisterWindowMessage("JumpList.ShowSettingsArg")

	Public Sub LoadStandardAPIMenu(ByVal Handle As IntPtr)
		' Get the Handle for the Forms System Menu
		Dim hMenu As IntPtr = NativeMethods.GetSystemMenu(Handle, False)

		' Create our new System Menu items just before the Close menu item
		NativeMethods.InsertMenu(hMenu, 5, NativeMethods.MF_BYPOSITION Or NativeMethods.MF_SEPARATOR, 0, String.Empty)
		' <-- Add a menu seperator
		NativeMethods.InsertMenu(hMenu, 6, NativeMethods.MF_BYPOSITION, NativeMethods.MF_SETTINGS, "Settings...")
		NativeMethods.InsertMenu(hMenu, 7, NativeMethods.MF_BYPOSITION, NativeMethods.MF_ABOUT, "About...")
		IsUsingStandardAPIMenu = True
	End Sub

	Public Sub LoadWindows7APIMenu(ByVal Handle As IntPtr)
		Dim list As JumpList = JumpList.CreateJumpListForIndividualWindow(TaskbarManager.Instance.ApplicationId, Handle)
		Dim current As FileInfo = New FileInfo(Application.ExecutablePath())
		list.ClearAllUserTasks()
		list.AddCustomCategories(New JumpListCustomCategory("Actions"))
		list.AddUserTasks(New JumpListLink(current.FullName, "Settings...") With {.Arguments = String.Format("Handle={0}|Command={1}", Handle, ShowSettingsArg), .IconReference = New IconReference(current.FullName, 0)})
		list.AddUserTasks(New JumpListLink(current.FullName, "About...") With {.Arguments = String.Format("Handle={0}|Command={1}", Handle, ShowAboutArg), .IconReference = New IconReference(current.FullName, 0)})
		list.AddUserTasks(New JumpListSeparator())
		list.Refresh()
		Windows7APIMenu = list
	End Sub

	Public Function RegisterMessage(ByVal MessageName As String) As Integer
		Return NativeMethods.RegisterWindowMessage(MessageName)
	End Function

	Public Sub SendMessage(ByVal Handle As IntPtr, ByVal MessageID As Integer)
		SendMessage(Handle, MessageID, IntPtr.Zero, IntPtr.Zero)
	End Sub

	Public Function SendMessage(ByVal Handle As IntPtr, ByVal MessageID As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
		Dim result As Long = NativeMethods.SendMessage(Handle, MessageID, wParam, lParam)
		Return result = 0
	End Function
End Module

Open in new window

5. Setup the main form to create the menu and overload the WndProc method.

Add the following to your main form code file (generally Form1.vb):
	Private Sub OnLoad(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
		If Environment.OSVersion.Platform = PlatformID.Win32NT Then
			If Environment.OSVersion.Version.Major < 6 Then
				LoadStandardAPIMenu(Handle)
			ElseIf Environment.OSVersion.Version.Major >= 6 Then
				If Environment.OSVersion.Version.Minor < 1 Then
					LoadStandardAPIMenu(Handle)
				ElseIf Environment.OSVersion.Version.Minor >= 1 Then
					LoadWindows7APIMenu(Handle)
				End If
			End If
		End If
	End Sub

	Protected Overloads Overrides Sub WndProc(ByRef m As Message)
		If IsUsingStandardAPIMenu Then
			' Check if a System Command has been executed
			If m.Msg = NativeMethods.WM_SYSCOMMAND Then
				' Execute the appropriate code for the System Menu item that was clicked
				Select Case m.WParam.ToInt32()
					Case NativeMethods.MF_SETTINGS
						MessageBox.Show("Settings menu was clicked")
						Exit Select
					Case NativeMethods.MF_ABOUT
						MessageBox.Show("About menu was clicked")
						Exit Select
				End Select
			End If
		Else
			If m.Msg = ShowSettingsArg Then
				MessageBox.Show("Settings task list menu was clicked")
			ElseIf m.Msg = ShowAboutArg Then
				MessageBox.Show("About task list menu was clicked")
			End If
		End If

		MyBase.WndProc(m)
	End Sub

Open in new window

6. Handle your Application events for Startup and StartupNextInstance.

User generated imageIn the ApplicationEvents.vb add the following code:
		Public Sub OnStartup(ByVal sender As Object, ByVal e As StartupEventArgs) Handles Me.Startup
			For Each arg As String In e.CommandLine
				If arg.ToUpper().Contains("HANDLE=") Then
					Dim message = arg.ToUpper().Split(New String() {"|"c}, StringSplitOptions.RemoveEmptyEntries).Select(Function(pairs) pairs.Split("="c)).ToDictionary(Function(key) key(0).ToUpper(), Function(val) If(val.Length = 1, String.Empty, val(1)))
					Dim handle As Integer = -1
					Dim command As Integer = -1
					If Not message.TryGetValue("HANDLE", handle) Then handle = -1
					If Not message.TryGetValue("COMMAND", command) Then command = -1

					If handle <> -1 AndAlso command <> -1 Then
						SendMessage(handle, command)
					End If
				End If
			Next
		End Sub

		Public Sub OnStartupNextInstance(ByVal sender As Object, ByVal e As StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
			e.BringToForeground = True
			For Each arg As String In e.CommandLine
				If arg.ToUpper().Contains("HANDLE=") Then
					Dim message = arg.ToUpper().Split(New String() {"|"c}, StringSplitOptions.RemoveEmptyEntries).Select(Function(pairs) pairs.Split("="c)).ToDictionary(Function(key) key(0).ToUpper(), Function(val) If(val.Length = 1, String.Empty, val(1)))
					Dim handle As Integer = -1
					Dim command As Integer = -1
					If Not message.TryGetValue("HANDLE", handle) Then handle = -1
					If Not message.TryGetValue("COMMAND", command) Then command = -1

					If handle <> -1 AndAlso command <> -1 Then
						SendMessage(handle, command)
					End If
				End If
			Next
		End Sub

Open in new window

If done correctly, when you run the application on a Windows Vista or lower computer, you should see the standard menu as presented in the examples above.  Running on Windows 7/8/8.1/10 should give you the following:User generated imageClicking on either the Settings or About task list item should give you a message box:User generated imageProof of Concept on Windows 8.1:User generated imageProof of Concept on Windows 2003:User generated image-saige-
Avatar of PNRT

ASKER

Many thanks for you help.  Have closed the question and awarded the points (I should have been able to give you double for the excellent answer), but I just wondered if this is the same API Code pack that you were referring to?
 http://channel9.msdn.com/coding4fun/articles/Coding-4-Fun-Windows-7-Taskbar
I ask only because there are many articles stating that this has been retired and is no longer supported
http://archive.msdn.microsoft.com/