Windows 7 Recipe – Taskbar Single Instance

It has been a while since I posted anything on the Windows 7 for Developer blog, but it is good to be back. This is about the first of four recipes we are releasing in the upcoming weeks; the others will be about Restart and Recovery, Trigger Start Services, and Library Watcher.

The purpose of this blog is to share a library we created specifically for the Windows 7 Taskbar scenario. The Windows 7 Taskbar includes many new features, one of which is the Taskbar Jump-List that allows users to perform various application-related tasks without ever switching to the application window. By now, there are many applications that uses the Taskbar—Office 2010, IE9, other browsers, Messenger, and Microsoft Zune, among many other Microsoft and non-Microsoft applications.

The taskbar jump list task is nothing but a simple command line call. The API lets you specify a command line type string with any number of arguments you want to pass. The Windows shell simply executes this command line and, in most cases, launches another instance of the application. You can read more about Developing for the Windows 7 Taskbar – Jump into Jump Lists – Part 1Part 2, and Part 3.

However, some applications use the Windows 7 Taskbar jump list tasks as “commands” to the application to change the application status. For example, Windows Media Player surfaces a few tasks (Resume Previous Play List is one example) that, when the user activates them, instead of launching a second instance of media player, they execute the task itself. Windows Live Messenger exposes few tasks that let you control your availability status as illustrated in the following figure

image

 

 

 

 

 

 

 

 

 

 

 

Windows Live Messenger allows users to change their status without switching to the application.

The Taskbar Single Instance Recipe allows developers to easily develop applications that use Messenger-like tasks that change the state of the currently running instance, allowing it to react to incoming state-change notifications and act accordingly.

This blog shows you how to use the Windows 7 Taskbar Single Instance recipe. If you are interested in how the library works you’ll have to download the library and its documentation. You can download the native C++ implementation, or .NET one.

Some technical background

The Windows 7 Taskbar jump list tasks API is simple. All you need to do is populate a list of IShellItems orIShellLinks. Each entry contains a “command” line with an optional argument. However, it should be noted that simply activating an IShellItem or an IShellLink from the Windows 7 Taskbar results in a new process being launched (usually seen by the user as a new instance of the application). If the shell link references your own executable, then activating it would result in the launching of a new instance of your process. Sometimes this is the required result (for example, when opening a new document in a new Notepad window). However we may want to use the Taskbar tasks to affect our current instance's state (for example, by changing the status of a messenger application).

Just to be clear, every time you click a Windows 7 jump list task, Windows creates a new process and runs the command line. There is no API for you to “communicate” through the Windows 7 Taskbar jump list tasks. In order to “communicate” with your application from the Taskbar jump list tasks, you’ll need to run some code in your application that upon application startup checks if the application is already running and, if it is running, notifies the currently executing application instance of the required task clicked by the user, and then closes the new instance that was just created.

This would usually involve some sort of synchronization object to check if an instance is already running (for example, a Mutex) and a communication mechanism to perform the notification itself. Basically, this is an implementation of a singleton pattern.

The code for accomplishing this task is usually quite similar for many application types, and is usually written over and over again. Therefore, the goal of the Taskbar Single Instance recipe is to provide a simple, reusable code library for both native code and managed code developers that, on the one hand, can be easily used with default settings, but also can be easily customized and extended if required on the other hand.

You can download the recipe code and documentation to fully understand how to implement the singleton pattern and see some examples. For now, let’s focus on how to use the library.

What Does the Recipe Do to My Applications?

Now that we understand the problem, let’s see the solution in action and then dive into the actual code. When you run any of the samples in the recipe and use the jump list tasks to change the status of the application, you’ll notice that the currently running application changes it status, but you will not see a second instance of the application launch. To see what happens behind the scenes you’ll need to use Process Explorer (part of System Internals). With this tool, you are able to see each process in your system. New processes are highlighted in green for few seconds, and processes that are being closed are highlighted in red.

With that in mind let’s review how the taskbar singleton recipe works. Our starting point is an application that is already running. As you can see from the following figure, the small top window is our sample application, and the bigger window is Process Explorer. In the box outlined in red, you can see two separate lines of the WpfSample.exe process. One is highlighted in yellow – this is our main process or the running application as you can see in the small green Window1 at the top of the figure.

image

 

 

 

 

 

 

 

 

 

 

The second WpfSample.exe process, highlighted in green, is the new process that was created as a result of pressing the jump list “Online” task to change the application status from its previous state to “Online.” As you can see, Windows launched a second instance of the application, but you can’t see it in the window – the application is not visible yet.

Next, the recipe kicks in and will “send” the Online message from the newly created instance of the application to the already existing one. Then the recipe will make sure the newly created instance is terminated. As you can see from the following figure, the second line is highlighted in red, and the process is terminated.

image

 

 

 

 

 

 

 

 

 

 

I Am Convinced; How Can I Use This?

The SingleInstanceManager class is the key class for using the recipe's code. In order to use the recipe, create an instance of the SingleInstanceManager using the static Initialize() method. This method accepts an object of type SingleInstanceManagerSetup that can be used to customize the behavior of theSingleInstanceManager. If you don’t customize, the SingleInstanceManager will use the default value provided in this blog. At a minimum, this setup object should be given an application ID that will be used to identify the application as a single instance.

The SingleInstanceManager supports both Managed and Native code. The samples I’ll be showing are in native (C++ code).

   1: SingleInstanceManagerSetup simSetup(L"MyApp");
   2: g_SIM = SingleInstanceManager::Initialize(&simSetup);
   3:  
   4: /* Your application code here */
   5:  
   6: // Free memory
   7: delete g_SIM;

As long as the SingleInstanceManager object is kept alive, no other instance of the application will be executed in parallel to the first instance. If and when you try to launch a second application instance, by default the process of the second application will exit before executing any of you application code. If you prefer, you can set up the SingleInstanceManager to throw an exception instead of exiting the process (see section "Additional Configuration Options").

Accepting Notifications from Other Application Instances

In order to receive incoming arguments from additional launched application instances, you need to specify an arguments handler when creating the SingleInstanceManagerSetup object.

   1: void ArgsHandler(LPCWSTR* pArguments, DWORD dwLength, LPVOID pContext)
   2: {
   3:     // Handle incoming arguments
   4: }
   5:  
   6: int _tmain(int argc, _TCHAR* argv[])
   7: {
   8:     // The NULL argument is the context passed to the handler (e.g. an object
   9:     // instance to call one of it's methods).
  10:     SingleInstanceManagerSetup simSetup(L"MyApp", ArgsHandler, NULL);
  11:     g_SIM = SingleInstanceManager::Initialize(&simSetup);
  12:  
  13:     /* Your application code here */
  14:  
  15:     // free memory
  16:     delete g_SIM;
  17:     return 0;
  18: }

By default, whenever trying to initialize a SingleInstanceManager object in a second process (as part of launching a new instance of an application) while the first application’s instance already exists, the command line arguments of the second instance will be sent to the first instance which will be notified of the arguments by the handler it has configured. The arguments sent to the first instance can be customized via the setup object (see section "Additional Configuration Options"). This setup object gives you full control over the how the parameters are being sent between applications, as well as other notifications.

Additional Configuration Options

The SingleInstanceManager behavior can be customized via various properties of the setup object (SingleInstanceManagerSetup). These properties are outlined in this section. Note that in Native C++, these properties are accessible via "Set…()" and "Get…()" methods and can also be set via the class constructor.

  • The ApplicationId property is used to identify an application and is used as the key by which the application's single instance will be identified. Note that an application ID is unique to each logged on user (that is, if two application instances are running under different users on the same machine, the application IDs will not match). The ApplicationId is the only mandatory property when constructing a SingleInstanceManagerSetup object.
  • The ArgumentsHandler property is used to specify a handler method to receive notifications of incoming arguments (for an example, see section "Accepting Notifications from other Application Instances").
  • The Context property is available only in native code, and is used to specify a context object to be passed to the arguments handler.
   1: void ArgsHandler(LPCWSTR* pArguments, DWORD dwLength, LPVOID pContext)
   2: {
   3:     MyClass *pClass = (MyClass *) pContext;
   4:     pClass->SomeMethod(...);    // Handle incoming arguments
   5: }
   6:  
   7: int _tmain(int argc, _TCHAR* argv[])
   8: {
   9:     MyClass *pClass = new MyClass();
  10:     SingleInstanceManagerSetup simSetup(L"MyApp", ArgsHandler, pClass);
  11:     g_SIM = SingleInstanceManager::Initialize(&simSetup);
  12:  
  13:     // Your application code here
  14:  
  15:     delete g_SIM;
  16:     delete pClass;
  17:     return 0;
  18: }
  • The TerminationOption property allows the user to choose a course of action in cases where an application instance is already running and the current instance needs to be closed (after notifying the original instance of the arguments). The enum allows two possible values:
  • Exit (default) – Exit the current process without returning to the user code. The managed code calls "System.Environment.Exit()" and the native code calls "ExitProcess()", and both use the specified exit code in the ExitCode property.
  • Throw –Throws an exception indicating that another application instance is already running. The user is then able to catch the exception and exit gracefully.
  • The ExitCode property allows specifying the exit code to be used in case the TerminationOptionproperty is set to "Exit" and an application instance is already running. The default value is 0.
  • The ArgumentsProvider property allows you to override the default response for the arguments passed to the first application instance. By default, the arguments passed are the command line arguments of the current process, which uses "Environment.GetCommandLineArgs()" for managed code and "GetCommandLineW()" and "CommandLineToArgvW()" for native code.
  • The Factory property allows the user to customize the argument delivery strategy that is used by theSingleInstanceManager. By default, the "RemotingStrategyFactory" is used for managed code and "NamedPipeStrategyFactory" is used for native code. These factory objects produce "RemotingStrategy" strategy objects and "NamedPipeStrategy" strategy objects respectively.

The delivery strategy factory is the class used by the SingleInstanceManager to both:

  • Notify the original application instance of the command line arguments, if this application instance is not the first one
  • Receive notification of incoming command line arguments from other application instances if the current instance is the first one

The specific strategy implementation contains all the logic and code that is required to enable the communication between the two processes. If the default supplied strategy does not meet the user's needs, the user can supply another implementation that can be easily plugged in.

  • The ArgumentsHandlerInvoker is the object used by the SingleInstanceManager to invoke the registered argument handler. By default, the handler is invoked on the thread pool in managed code and directly on the incoming thread in native codeInstance Notification Option
  • The InstanceNotificationOption property is used to determine the behavior when the first application instance is launched under elevated security rights (that is, run as administrator), and the second instance is not an administrator. This enum has two options:
  • NotifyAnyway (default) – Notifies the original application instance anyway.
  • NotifyOnlyIfAdmin – Notifies the first application instance only if the second application instance is also run as an administrator.
  • The DeliveryFailureNotification is used to provide the SingleInstanceManager with a callback method if the delivery of the arguments from the second process to the first one was unsuccessful.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值