Minimize to a single instance application in the system tray when it is disabled

Source: Internet
Author: User

Original article: http://www.codeproject.com/csharp/SingleInstanceApplication.asp
Translation: Anders Liu
Source: http://www.cnblogs.com/AndersLiu/archive/2007/07/09/811354.html

Introduction

This article solves the following problems:
1. Create a single-instance applicationProgram.
2. When you try to start a new instance, restore the previous instance.
3. Minimize the window close time to the notification area of the taskbar (with animation ).

How do I create a single-instance application?

In general, make sure that only one instance of the program is running at any time. If the user tries to run another instance, or notifies the user that there is already an instance, or activates the previous running instance and brings it to the foreground. For Windows applications, we may want to restore the existing main window. Therefore, when the application starts, check whether there are running instances. If yes, you should exit the current instance, activate the main window of the previous instance, and display it to the user.

The mutex (Mutual Exclusion semaphore) [mutex (mutex semaphore)] can be used to make an application into a single instance. Windows applications load the form using the application. Run () method. In the main method, create a new mutex. If you can create a new mutex, the application is allowed to run. If mutex has been created, the application will not start. This ensures that only one instance is running for any use.

// Checks whether a new mutex is created.
Bool newmutexcreated = false;
// The mutex name is prefixed with local,
// Make sure that it is created in the namespace of each session (Per-session,
// Instead of in the global namespace.
String mutexname = "Local \" +
System. reflection. Assembly. getexecutingassembly (). getname (). Name;

Mutex = NULL;
Try
{
// Create a new mutex with a unique name
Mutex = new mutex (false, mutexname, out newmutexcreated );
}
Catch (exception ex)
{
MessageBox. Show (ex. Message + "\ n" + ex. stacktrace +
"\ N" + "application exiting...", "exception thrown ");
Application. Exit ();
}

// If mutex is created for the first time, start the application instance,
// Because this is the first time
If (newmutexcreated)
{
Application. Run (New animatedwindowform ());
}

When creating a mutex, its name prefix can be Globa \ or local \. The Global \ prefix indicates that the mutex affects the global namespace.

Prefix with local \ indicates that the mutex only affects the user session namespace.

Windows XP and Windows 2003 allow users to quickly switch through Terminal Services sessions. Therefore, if mutext uses the Global \ prefix, only one instance can run throughout the system. If a user starts the application, other users cannot create another instance in their sessions. If the mutext prefix is not local \, it only affects each session.

To learn about the kernel object namespace, read this msdnArticleHttp://msdn.microsoft.com/library/en-us/termserv/termserv/kernel_object_namespaces.asp ).

Now, there is another task to complete-move the previous instance to the foreground. In a Windows application, this means that the main window of the application is restored to the top. If it is hidden, it is displayed to the user.

Restore the previous instance

To restore the main window, you must obtain the handle of the Main Window of the application. Use the following sectionCodeYou can obtain the maindomainwhandle of the process:

Process [] currentprocesses =
Process. getprocessesbyname ("singleinstanceapplication ");
System. intptr main1_whandle = currentprocesses [0]. main1_whandle;
If (main1_whandle! = Intptr. Zero)
{
Showwindow (main1_whandle, sw_restore); // restore the window
Updatewindow (main1_whandle );
}

However, when the application's main window is hidden, this Code fails because the handle returns 0.

A reliable mechanism is to make maindomainwhandle necessary. This is the turn of shared memory. Shared Memory is a method of IPC (Inter Process Communication). Using this method, two or more processes can use shared memory fragments for communication. You can use Win32 API to create shared memory in C. Memory ing can associate the file content with a specific address area in your process address space, system page file, or system memory address.

To share data between two processes, you need to create a shared memory in the system page file.

To enable a process to share data to other processes through the memory mapped file (MMF), each process must access the file. This is achieved by setting a name for the MMF object. Every process can use this name to access the shared memory.

Private const int invalid_handle_value =-1;
Private const int file_map_write = 0x2;
Private const int file_map_read = 0x0004;

[Dllimport ("kernel32.dll", entrypoint = "openfilemapping ",
Setlasterror = true, charset = charset. Auto)]
Private Static extern intptr openfilemapping (int
Wdesiredaccess, bool binherithandle, string lpname );

[dllimport ("kernel32.dll", entrypoint = "createfilemapping",
setlasterror = true, charset = charset. Auto)]

Private Static extern intptr createfilemapping (INT hfile,
intptr lpattributes, uint flprotect,
uint dwmaximumsizehigh, uint dwmaximumsizelow,
string lpname );
[dllimport ("kernel32.dll")]
Private Static extern intptr mapviewoffile (intptr hfilemappingobject,
uint dwdesiredaccess, uint dwfileoffsethigh,
uint dwfileoffsetlow, uint idle);
[dllimport ("kernel32.dll", entrypoint = "unmapviewoffile",
setlasterror = true, charset = charset. auto)]
Private Static extern bool unmapviewoffile (intptr lpbaseaddress);

[Dllimport ("kernel32.dll", entrypoint = "closehandle ",
Setlasterror = true, charset = charset. Auto)]
Private Static extern bool closehandle (uint hhandle );
[Dllimport ("kernel32.dll", entrypoint = "getlasterror ",
Setlasterror = true, charset = charset. Auto)]

Private Static extern uint getlasterror ();
Private intptr memoryfilehandle;

Public Enum fileaccess: int
{
Readonly = 2,
Readwrite = 4
}

Create a new MMF for the shared memory object. You can use the createfilemapping () function. After a new MMF object is created, a part of the system page file is retained.

Parameters
    • Hfile: The file handle for memory ing. When creating an mmf in the system page file, the value must be 0 xffffffff (-1 ).
    • Lpattributes -- pointer to a security_attributes struct
    • Flprotect -- the protection type specified for the memory ing file.
      • Page_readonly -- read-only access.
      • Page_readwrite -- read/write access.
      • Page_writecopy -- copy-on-write access.
      • Page_execute_read -- read and execute access.
      • Page_execute_readwrite -- read, write, and execute access.
    • Dwmaximumsizehigh -- the highest DWORD Value of the maximum size of the file ing object.
    • Dwmaximumsizelow -- the minimum DWORD Value of the maximum size of the file ing object.
    • Lpname -- name of the file ing object.

Public static memorymappedfile createmmf (string filename, fileaccess access, int size)
{
If (size <0)
Throw new argumentexception ("the size parameter" +
"Shocould be a number greater than zero .");

Intptr memoryfilehandle = createfilemapping (0 xffffffff,
Intptr. Zero, (uint) Access, 0, (uint) size, filename );

If (memoryfilehandle = intptr. Zero)
Throw new sharedmemoryexception ("creating shared memory failed .");

Return new memorymappedfile (memoryfilehandle );
}

Next we start the first instance of the application and create an MMF object.

// When creating mutex for the first time, run the program because it is the first instance.
If (newmutexcreated)
{
// Create the shared memory to store the window handle.
Lock (typeof (animatedwindowform ))
{
Sharedmemory = memorymappedfile. createmmf ("Local \" +
"Sharedmemoryanimatedwindow ",
Memorymappedfile. fileaccess. readwrite, 8 );
}
Application. Run (New animatedwindowform ());
}

Once the handle of the memory ing file is obtained, you can use it to map the File View to the address space of the calling process. As long as the MMF object is stored, the view can be mapped and unmapped. Mapviewoffile () and unmapviewoffile () functions are used to map and unmap views. Whether the read/write operations can be performed depends on the access type specified when mapviewoffile () function is called.

Mapviewoffile () parameters:

    • Hfilemappingobject -- the handle of the MMF object. The createfilemapping and openfilemapping functions can return this handle.
    • Dwdesiredaccess -- access type of MMF object. This parameter can be set to the following values:
      • File_map_read -- read-only access. The MMF object must be accessed by page_readwrite or page_readonly.
      • File_map_write -- read/write access. The MMF object must have page_readwrite access.
      • File_map_copy -- copy-on-write access. The MMF object must be accessed by page_writecopy.
      • File_map_execute -- execute access. The MMF object must be accessed by page_execute_readwrite or page_execute_read.
    • Dwfileoffsethigh -- the DWORD high of the starting offset of the ing view in the file.
    • Dwfileoffsetlow -- the DWORD low position of the starting offset of the ing view in the file.
    • Dwnumberofbytestomap -- number of bytes mapped to the view by file ing.

After creating a view of the memory ing file, you can call the unmapviewoffile () function at any time to cancel the ing. The only required parameter is the handle of the ing view.

Unmapviewoffile (mappedviewhandle );

To write data to the shared memory, you must first use file_map_write to create a mming view for the MMF object. Because we want to write this handle in the main window, we can use the marshal. writeintptr () method to write the shared memory. After the write operation is complete, cancel the ing view and call the closehandle () function to release the ing view.

Public void writehandle (intptr ?whandle)
{
Intptr mappedviewhandle = mapviewoffile (memoryfilehandle,
(Uint) file_map_write, 0, 0, 8 );
If (mappedviewhandle = intptr. Zero)
Throw new sharedmemoryexception ("Creating" +
"A View of shared memory failed .");

Marshal. writeintptr (mappedviewhandle, windowhandle );

Unmapviewoffile (mappedviewhandle );
Closehandle (uint) mappedviewhandle );
}

To read the shared memory, use file_map_read to access the mming view of the created MMF object. Use the marshal. readintptr () method to read shared memory. After the read operation, cancel the ing view and call the closehandle () function to release the ing view.

Public static intptr readhandle (string filename)
{
Intptr mappedfilehandle =
Openfilemapping (INT) fileaccess. readwrite, false, filename );

If (mappedfilehandle = intptr. Zero)
Throw new sharedmemoryexception ("opening the" +
"Shared memory for read failed .");

intptr mappedviewhandle = mapviewoffile (mappedfilehandle,
(uint) file_map_read, 0, 0, 8);
If (mappedviewhandle = intptr. zero)
throw new sharedmemoryexception ("Creating" +
"a view of shared memory failed. ");

Intptr restart whandle = marshal. readintptr (mappedviewhandle );
If (when whandle = intptr. Zero)
Throw new argumentexception ("reading from the specified" +
"Address in shared memory failed .");

Unmapviewoffile (mappedviewhandle );
Closehandle (uint) mappedfilehandle );
Return windowhandle;
}

After the application Main Window handle is created, we write it into the shared memory.

Protected override void onhandlecreated (eventargs E)
{
Base. onhandlecreated (E );
Intptr main1_whandle = This. Handle;
Try
{
Lock (this)
{
// Write the handle to the shared memory
Sharedmemory. writehandle (main1_whandle );
}
}
Catch (exception ex)
{
MessageBox. Show (ex. Message + "\ n" + ex. stacktrace +
"\ N" + "application exiting...", "exception thrown ");
Application. Exit ();
}
}

When you try to start the second instance of the application, you can obtain the window handle of the previous instance from the shared memory and use the showwindow () and updatewindow () functions to restore the main window.

// If mutex already exists, you do not need to start a new instance of the application.
// because the previous instance is already running.
try
{
// obtain the handle of the Main Window of the program.
// The handle is stored in the shared memory by the previous instance.
intptr main1_whandle = system. intptr. zero;
lock (typeof (animatedwindowform)
{< br> main1_whandle = memorymappedfile. readhandle ("local" +
"file: // sharedmemoryanimatedwindow/");
}

If (main1_whandle! = Intptr. Zero)
{
// Restore the window
Showwindow (main1_whandle, sw_restore );
Updatewindow (main1_whandle );
}
}
Catch (exception ex)
{
MessageBox. Show (ex. Message + "\ n" + ex. stacktrace +
"\ N" + "application exiting...", "exception thrown ");
}

Therefore, the main method of our application looks like this:

static void main ()
{< br> // used to check if we can create a new mutex
bool newmutexcreated = false;
// The Name Of The mutex is to be prefixed with local \ To make
// sure that its is created in the per-session
/namespace, not in the global namespace.
string mutexname = "Local \" +
system. reflection. assembly. getexecutingassembly (). getname (). name;

Mutex = NULL;
Try
{
// Create a new mutex object with a unique name
Mutex = new mutex (false, mutexname, out newmutexcreated );
}
Catch (exception ex)
{
MessageBox. Show (ex. Message + "\ n" + ex. stacktrace +
"\ N" + "application exiting...", "exception thrown ");
Application. Exit ();
}

// When the mutex is created for the first time
// We run the program since it is the first instance.
If (newmutexcreated)
{
// Create the shared memory to store the window
// Handle. This memory is shared between processes
Lock (typeof (animatedwindowform ))
{
Sharedmemory = memorymappedfile. createmmf ("local" +
"File: // sharedmemoryanimatedwindow /",
Memorymappedfile. fileaccess. readwrite, 8 );
}
Application. Run (New animatedwindowform ());
}
Else
// If the mutex already exists, no need to launch
// A new instance of the program because
// A previous instance is running.
{
Try
{
// Get the program's main window handle,
// Which was previusly stored in shared memory.
Intptr main1_whandle = system. intptr. zero;
Lock (typeof (animatedwindowform ))
{
Main1_whandle =
Memorymappedfile. readhandle ("local" +
"File: // sharedmemoryanimatedwindow /");
}
If (main1_whandle! = Intptr. Zero)
{
// Restore the window
Showwindow (main1_whandle, sw_restore );
Updatewindow (main1_whandle );
}
Return;
}
Catch (exception ex)
{
MessageBox. Show (ex. Message + "\ n" + ex. stacktrace +
"\ N" + "application exiting...", "exception thrown ");
}
// Tell the Garbage Collector to keep the mutex alive
// Until the code execution reaches this point,
// Ie. Normally when the program is exiting.
GC. keepalive (mutex );
// Release the mutex
Try
{
Mutex. releasemutex ();
}
Catch (applicationexception ex)
{
MessageBox. Show (ex. Message + "\ n" + ex. stacktrace,
"Exception thrown ");
GC. Collect ();
}
}
}

Minimize the window to the notification area

This includes four tasks:

The first step is to prevent users from closing the window when they click the close button, override the protected virtual onclosing method, and cancel the Close event. The form should be hidden, and the application runs in the background. But what happens when the user shuts down the system? The operating system will send a close message like all open windows. If our application refuses to close the window, the system will not close it and it will wait until all windows are closed. Therefore, we need to rewrite wndproc to process wm_queryendsession messages.

Protected override void onclosing (canceleventargs E)
{
If (systemshutdown = true)
E. Cancel = false;
Else
{
E. Cancel = true;
This. animatewindow ();
This. Visible = false;
}
}

Protected override void wndproc (ref message m)
{
// Once the program receives the wm_queryendsession message,
// Set the Boolean value of systemshutdown to true.

If (M. MSG = wm_queryendsession)
Systemshutdown = true;
Base. wndproc (ref m );
}

Next, we want to display a notification icon in the notification area of the taskbar. Add a policyicon control to the main form and set the icon for it. The icon is displayed in the notification area of the taskbar. Our next goal is to achieve the animation of the window moving closer to the notification area. Before performing this animation, make sure that you do not disable the window animation in the system. You can enable or disable window animation by setting the minanimate key in hkeycurrentuser \ Control Panel \ Desktop. We check this value and set a Boolean Value Based on your preference.

Registrykey animationkey =
Registry. currentuser. opensubkey ("Control Panel" +
"File: // desktop // windowmetrics % 22, true );
Object animkeyvalue = animationkey. getvalue ("minanimate ");

If (system. Convert. toint32 (animkeyvalue. tostring () = 0)
This. animationdisabled = true;
Else
This. animationdisabled = false;

If animations can be used, we use the drawanimatedrects (intptr hwnd, int idani, ref rect lprcfrom, ref rect lprcto) function to draw window animations. This function has four parameters. Hwnd is the window handle for animation. Idani is an animation type. If idani_caption is specified, the title of the window will be moved from the position specified by lprcfrom in an animated way to the position specified by lprcto. Otherwise, it draws an external box rectangle and animations it. Both lprcfrom and lprcto are of the rect type and specify the start and end rectangles of the animation. We use the getwindowrect (intptr hwnd, ref rect lprect) function to obtain the rectangle from the form handle. When minimized, the start position is the rect of the window. The end position is the rect of the notification area. Therefore, the next task is to get the handle of the notification area. The Class Name of the taskbar is shell_traywnd. The taskbar contains many other subwindows. We need the "Notification area" handle, which contains the notification icon. We can obtain the handle by enumerating the Child Window of shell_traywnd. Now we can use the getwindowrect (intptr hwnd, ref rect lprect) function to obtain the rect of the notification area.

Private void animatewindow ()
{
// If the user has not disabled animating windows...
If (! This. animationdisabled)
{
Rect animatefrom = new rect ();
Getwindowrect (this. Handle, ref animatefrom );

Rect animateto = new rect ();
Intptr policyareahandle = getnotificationareahandle ();

If (policyareahandle! = Intptr. Zero)
{
If (getwindowrect (policyareahandle, ref animateto) = true)
{
Drawanimatedrects (this. handle,
Idani_caption, ref animatefrom, ref animateto );
}
}
}
}

Private intptr getnotificationareahandle ()
{
Intptr hwnd = find1_wex (intptr. Zero, intptr. Zero, "shell_traywnd", null );
Hwnd = find1_wex (hwnd, intptr. Zero, "traypolicywnd", null );
Hwnd = find1_wex (hwnd, intptr. Zero, "syspager", null );

If (hwnd! = Intptr. Zero)
Hwnd = find1_wex (hwnd, intptr. Zero, null, "notification area ");

Return hwnd;
}

Conclusion

It is true that obtaining the window handle of the notification area sometimes fails, because "traypolicywnd", "syspager", and "notification area" are all window class names of unclassified ented (non-linear, it may be changed in the future Windows version.

Known issues

There is a conflict between the debug and release versions of the application. If you first start the release version and then start the debug version, both instances will run. Mutex cannot start a second instance at the beginning.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.