Compared with the development of the taskbar feature in the previous article, the development of jumplist is a little troublesome. Jumplist will be divided into two explanations. This will explain how to add a user task first ). Taking foobar2000 as an example, when you right-click the taskbar button, the jumplist of the program is displayed.
The three items at the bottom are system tasks, which generally do not need to be operated. The preceding two tasks are playback and Parameter options, that is, Custom User Tasks. A user task is essentially a shortcut, which is represented by the ishelllink interface in the program.
1. icustomdestinationlist Interface
Create a window first, then add a createjumplist method, and create a jumplist in this method. To create a jumplist, follow these steps: 1. Create a jumplist
Icustomdestinationlist
Interface, which corresponds to jumplist. 2. CallBeginlist
Method. 3. CreateIobjectcollection
Interface. 4. Forward
Iobjectcollection
. 5.
Iobjectcollection
Interface acquisitionIobjectarray
Interface. 6. Set
Iobjectarray
Join
Icustomdestinationlist
. 7. CallCommitlist
Method. Add the following code to the createjumplist method:
Void createjumplist () <br/>{< br/> hresult hr; <br/> // create a list <br/> icustomdestinationlist * plist = NULL; <br/> hR = cocreateinstance (clsid_destinationlist, null, <br/> clsctx_inproc_server, iid_ppv_args (& plist); <br/> If (succeeded (HR )) <br/>{< br/> // beginlist <br/> uint uminslots; <br/> iobjectarray * poaremoved = NULL; <br/> hR = plist-> beginlist (& uminslots, iid_ppv_args (& poaremoved); <br/> If (succeeded (HR )) <br/> {<br/> // objectcollection <br/> iobjectcollection * poctasks = NULL; <br/> hR = cocreateinstance (clsid_enumerableobjectcollection, null, <br/> clsctx_inproc_server, iid_ppv_args (& poctasks); <br/> If (succeeded (HR) <br/>{< br/> hR = addshelllink (poctasks ); <br/> If (succeeded (HR) <br/>{< br/> // objectarray <br/> iobjectarray * poatasks = NULL; <br/> hR = poctasks-> QueryInterface (iid_ppv_args (& poatasks); <br/> If (succeeded (HR )) <br/>{< br/> hR = plist-> addusertasks (poatasks); <br/> If (succeeded (HR )) <br/>{< br/> hR = plist-> commitlist (); <br/>}< br/> poatasks-> release (); <br/>}< br/> poctasks-> release (); <br/>}< br/> poaremoved-> release (); <br/>}< br/> plist-> release (); <br/>}< br/>}
Ii. ishelllink Interface
The code above cannot be compiled, and the addshelllink method has not been compiled. This method is used inIobjectcollection
AddIshelllink
Object.
Ishelllink
The interface has several methods for setting properties: 1,Setpath
: Set the target path. 2,Setworkingdirectory
Set the working directory. 3,Seticonlocation
Set the icon. 4,Setarguments
SET command line parameters. 5. Set the title (this step is a little more complex and configured in an independent method ). Call
Iobjectcollection
OfAddobject
Method to add the shortcut.
Hresult addshelllink (iobjectcollection * poctasks) <br/> {<br/> hresult hr; <br/> // create shelllink <br/> ishelllink * pslautorun = NULL; <br/> hR = cocreateinstance (clsid_shelllink, null, <br/> clsctx_inproc_server, iid_ppv_args (& pslautorun); <br/> If (succeeded (HR )) <br/> {<br/> // Application Path <br/> hR = pslautorun-> setpath (_ T ("C: // windows // notepad.exe "); <br/> If (succeeded (HR )) <br/>{< br/> hR = pslautorun-> setworkingdirectory (_ T ("C: //"); <br/> If (succeeded (HR )) <br/> {<br/> // icon <br/> hR = pslautorun-> seticonlocation (_ T ("C: // windows // notepad.exe "), 0); <br/> If (succeeded (HR )) <br/>{< br/> // command line parameter <br/> hR = pslautorun-> setarguments (_ T ("test.txt ")); <br/> If (succeeded (HR) <br/>{< br/> hR = settitle (pslautorun, _ T ("Notepad ")); <br/> If (succeeded (HR) <br/>{< br/> hR = poctasks-> addobject (pslautorun ); <br/>}< br/> pslautorun-> release (); <br/>}< br/> return hr; <br/>}
The settitle method is used to set the title.Ishelllink
The interface itself does not contain a method to set the title. Therefore, another interface is required.Ipropertystore
To set the title. First
Ishelllink
Interface
Ipropertystore
Interface, and then initializePropvariant
Object.
Propvariant
Set the object as the title and submit it.
Hresult settitle (ishelllink * pshelllink, lpctstr sztitle) <br/>{< br/> hresult hr; <br/> // Title <br/> ipropertystore * PPS = NULL; <br/> hR = pshelllink-> QueryInterface (iid_ppv_args (& PPS); <br/> If (succeeded (HR )) <br/>{< br/> propvariant pvtitle; <br/> hR = initpropvariantfromstring (sztitle, & pvtitle); <br/> If (succeeded (HR )) <br/>{< br/> hR = PPS-> setvalue (pkey_title, pvtitle); <br/> If (succeeded (HR )) <br/>{< br/> hR = PPS-> commit (); <br/>}< br/> propvariantclear (& pvtitle ); <br/>}< br/> PPS-> release (); <br/>}< br/> return hr; <br/>}
In the above example, the target path is set to the path of notepad, and the command line parameter "test.txt" is added. When you click it, the notepad is called and the parameter "test.txt" is input ".
3. The current program instance responds to User Tasks
The problem now is that in most cases, user tasks should not only correspond to a shortcut pointing to a program, but also correspond to a function of the current program. For example, the parameter options in foobar2000 correspond to the functions in the program. When this user task is selected, the current instance of the current program should respond to this operation, rather than the new instance. The next step is to reflect user tasks to the current program instance.
Two problems are involved here. The first problem is that the program must be a single-instance application, because when you click a user task, we cannot respond to the new program instance. When the program is started, check whether the program has been running on previous instances. If yes, exit to prevent the second instance from running. The second problem is that our user tasks are actually reflected by the parameters passed to the program through shortcuts. This parameter can only be received by the second instance. Before the second instance exits, you need to pass the parameter to the first instance.
We should first point the previously created user task to ourselves. The addshelllink modification method is as follows, where the comment is changed:
Hresult addshelllink (iobjectcollection * poctasks) <br/> {<br/> hresult hr; <br/> // create shelllink <br/> ishelllink * pslautorun = NULL; <br/> hR = cocreateinstance (clsid_shelllink, null, <br/> clsctx_inproc_server, iid_ppv_args (& pslautorun); <br/> If (succeeded (HR )) <br/> {<br/> // Application Path <br/> // hR = pslautorun-> setpath (_ T ("C: // windows // notepad.exe "); <br/> // obtain the Application Path <br/> tchar szpath [max_path]; <br/> getmodulefilename (getmodulehandle (null), szpath, max_path); <br/> hR = pslautorun-> setpath (szpath ); <br/> If (succeeded (HR) <br/>{< br/> // hR = pslautorun-> setworkingdirectory (_ T ("C: // "); <br/> If (succeeded (HR )) <br/> {<br/> // icon <br/> // hR = pslautorun-> seticonlocation (_ T ("C: // windows // notepad.exe "), 0); <br/> hR = pslautorun-> seticonlocation (szpath, 0); <br/> If (succeeded (HR )) <br/> {<br/> // command line parameter <br/> // hR = pslautorun-> setarguments (_ T ("test.txt ")); <br/> hR = pslautorun-> setarguments (_ T ("/task1"); <br/> If (succeeded (HR )) <br/>{< br/> // hR = settitle (pslautorun, _ T ("Notepad"); <br/> hR = settitle (pslautorun, _ T ("User Task 1"); <br/> If (succeeded (HR )) <br/>{< br/> hR = poctasks-> addobject (pslautorun ); <br/>}< br/> pslautorun-> release (); <br/>}< br/> return hr; <br/>}
Now, when we click User Task 1, a new instance is started. Next we will solve the problem mentioned above.
The solution to the first problem is to use mutex (mutex ). When the first instance is started, a mutex is created. This mutex is also created when the second instance is started. Because the mutex has been created, it will inevitably lead to failure and the second instance will exit. When the first instance ends, cancel the mutex. Add the following code at the beginning of the winmain function:
Handle hmutex = createmutexex (null, _ T ("Local // mutextestjumplist"), 0, 0); <br/> If (hmutex = NULL) <br/> {// mutex creation failed. A previous instance is running and the current instance is exited. <br/> return 0; <br/>}
Add the following code to the end of the winmain function:
If (hmutex! = NULL) <br/>{< br/> closehandle (hmutex); <br/>}
This time, when we click User Task 1, we can't see the start of the new instance (in fact, the new instance is still started, but quickly exited after detecting the previous instance ).
The second problem is how to pass parameters to the first instance. For Windows programs, the easiest thing to think of is sending messages. But how can I get the main window handle of the previous instance? Memory mapped file is used here ). After the first instance runs, put its own Main Window handle into a memory ing file. The second instance reads the main window handle of the previous instance from the memory ing file. After the window is created, add the following code to put the current main window handle into the memory ing file:
// Put the main window handle into the memory ing file <br/> handle hmmffile = createfilemapping (invalid_handle_value, null, <br/> page_readwrite, 0, sizeof (g_hwnd ), _ T ("Local // mmftestjumplist"); <br/> lpvoid = mapviewoffile (hmmffile, file_map_read | file_map_write, 0, 0, 0 ); <br/> memcpy_s (lpvoid, sizeof (g_hwnd), & g_hwnd, sizeof (g_hwnd); <br/> unmapviewoffile (hmmffile );
The name of the memory ing file should not be the same as that of the previous mutex file; otherwise, creation will fail. Close the file before exiting the program:
Closehandle (hmmffile );
At the beginning of the winmain function, it is detected that a previous instance is running. Read the main window handle of the previous instance from the memory ing file:
// Obtain the handle of the Main Window of the previous instance <br/> handle hmmffile = openfilemapping (file_map_read, false, _ T ("Local // mmftestjumplist ")); <br/> If (hmmffile = NULL) return 0; <br/> lpvoid = mapviewoffile (hmmffile, file_map_read, 0, 0, 0 ); <br/> hwndprev = NULL; <br/> memcpy_s (& hwndprev, sizeof (hwndprev), lpvoid, sizeof (hwndprev); <br/> unmapviewoffile (hmmffile ); <br/> closehandle (hmmffile );
After obtaining the window handle of the previous instance, use the wm_copydata message to send the command line parameters to the previous instance.
// Send the command line parameters to the previous instance <br/> copydatastruct cpdata = {0}; <br/> cpdata. dwdata = 1; <br/> cpdata. lpdata = szcmdline; <br/> cpdata. cbdata = (_ tcslen (szcmdline) + 1) * sizeof (tchar); <br/> sendmessage (hwndprev, wm_copydata, 0, reinterpret_cast <lparam> (& cpdata ));
Then add the processing of the wm_copydata message:
Case wm_copydata: // command line parameters are sent from the next instance <br/>{< br/> copydatastruct * pdata = reinterpret_cast <copydatastruct *> (lparam ); <br/> If (pdata-> dwdata = 1) <br/> {<br/> lpctstr sz20.line = reinterpret_cast <lpctstr> (pdata-> lpdata ); <br/> If (szw.line! = NULL) <br/> handleshortline (szshortline); <br/>}< br/> return 0; <br/>}< br/> break;
In handleappsline, we can simply display only one message:
Void handleappsline (maid) <br/>{< br/> If (null! = Strstr (szcmdline, _ T ("/task1") <br/>{< br/> MessageBox (g_hwnd, _ T ("User Task 1 clicked "), null, mb_ OK); <br/>}< br/>}
Verify the current effect:
Because jumplist exists even when the program is not running, we need to call the handleappsline method once after the window is created so that the first program instance can respond to user clicks in the static state. The effect is as follows:
At this time, the program is not running, but it is locked to the taskbar. Click the user task on jumplist, the first program instance starts, and responds to the user task.
To simplify the problem, the above Code saves a lot of error handling. In actual use, I encapsulate the data transfer between a single instance and the instance in a secondary class, and the class will be provided in the source code.
The header file to be included in the above Code:
# Include <shlobj. h> <br/> # include <propvarutil. h> <br/> # include <propkey. h>
Database to be connected:Shlwapi. Lib
Download the source code in this section