Visual CSharp 2005 Recipes (2006) [eng]
.pdf138 C H A P T E R 4 ■ T H R E A D S, P R O C E S S E S, A N D S Y N C H R O N I Z AT I O N
Console.WriteLine(ex);
}
// Wait to continue. Console.WriteLine(Environment.NewLine); Console.WriteLine("Main method complete. Press Enter."); Console.ReadLine();
}
}
}
4-16. Terminate a Process
Problem
You need to terminate a process such as an application or a service.
Solution
Obtain a Process object representing the operating system process you want to terminate. For Windows-based applications, call Process.CloseMainWindow to send a close message to the application’s main window. For Windows-based applications that ignore CloseMainWindow, or for non-Windows- based applications, call the Process.Kill method.
How It Works
If you start a new process from managed code using the Process class (discussed in recipe 4-15), you can terminate the process using the Process object that represents the new process. You can also obtain Process objects that refer to other currently running processes using the static methods of the Process class summarized in Table 4-4.
Table 4-4. Methods for Obtaining Process References
Method |
Description |
GetCurrentProcess |
Returns a Process object representing the currently active process. |
GetProcessById |
Returns a Process object representing the process with the specified ID. |
|
This is the process ID (PID) you can get using Windows Task Manager. |
GetProcesses |
Returns an array of Process objects representing all currently active |
|
processes. |
GetProcessesByName |
Returns an array of Process objects representing all currently active |
|
processes with a specified friendly name. The friendly name is the name |
|
of the executable excluding file extension or path; for example, a friendly |
|
name could be notepad or calc. |
|
|
Once you have a Process object representing the process you want to terminate, you need to call either the CloseMainWindow method or the Kill method. The CloseMainWindow method posts a WM_CLOSE message to a Windows-based application’s main window. This method has the same effect as if the user had closed the main window using the system menu, and it gives the application the opportunity to perform its normal shutdown routine. CloseMainWindow will not terminate applications that
C H A P T E R 4 ■ T H R E A D S, P R O C E S S E S, A N D S Y N C H R O N I Z AT I O N |
139 |
do not have a main window or applications with a disabled main window—possibly because a modal dialog box is currently displayed. Under such circumstances, CloseMainWindow will return false.
CloseMainWindow returns true if the close message was successfully sent, but this does not guarantee that the process is actually terminated. For example, applications used to edit data will usually give the user the opportunity to save unsaved data if a close message is received. The user usually has the chance to cancel the close operation under such circumstances. This means CloseMainWindow will return true, but the application will still be running once the user cancels. You can use the Process.WaitForExit method to signal process termination and the Process.HasExited property to test whether a process has terminated. Alternatively, you can use the Kill method.
The Kill method simply terminates a process immediately; the user has no chance to stop the termination, and all unsaved data is lost. Kill is the only option for terminating Windows-based applications that do not respond to CloseMainWindow and for terminating non-Windows-based applications.
The Code
The following example starts a new instance of Notepad, waits 5 seconds, and then terminates the Notepad process. The example first tries to terminate the process using CloseMainWindow. If CloseMainWindow returns false, or the Notepad process is still running after CloseMainWindow is called, the example calls Kill and forces the Notepad process to terminate; you can force CloseMainWindow to return false by leaving the File Open dialog box open.
using System;
using System.Threading; using System.Diagnostics;
namespace Apress.VisualCSharpRecipes.Chapter04
{
class Recipe04_16
{
public static void Main()
{
// Create a new Process and run notepad.exe. using (Process process =
Process.Start("notepad.exe",@"c:\SomeFile.txt"))
{
//Wait for 5 seconds and terminate the notepad process. Console.WriteLine(
"Waiting 5 seconds before terminating notepad.exe."); Thread.Sleep(5000);
//Terminate notepad process.
Console.WriteLine("Terminating Notepad with CloseMainWindow.");
// Try to send a close message to the main window. if (!process.CloseMainWindow())
{
// Close message did not get sent - Kill Notepad. Console.WriteLine("CloseMainWindow returned false - " +
" terminating Notepad with Kill."); process.Kill();
}
else
{
//Close message sent successfully; wait for 2 seconds
//for termination confirmation before resorting to Kill.
140 C H A P T E R 4 ■ T H R E A D S, P R O C E S S E S, A N D S Y N C H R O N I Z AT I O N
if (!process.WaitForExit(2000))
{
Console.WriteLine("CloseMainWindow failed to" +
" terminate - terminating Notepad with Kill."); process.Kill();
}
}
}
// Wait to continue.
Console.WriteLine("Main method complete. Press Enter."); Console.ReadLine();
}
}
}
4-17. Ensure That Only One Instance of an Application Can Execute Concurrently
Problem
You need to ensure that a user can have only one instance of an application running concurrently.
Solution
Create a named System.Threading.Mutex object, and have your application try to acquire ownership of it at start-up.
How It Works
The Mutex provides a mechanism for synchronizing the execution of threads across process boundaries and in addition provides a convenient mechanism through which to ensure that only a single instance of an application is running concurrently. By trying to acquire ownership of a named Mutex at start-up and exiting if the Mutex cannot be acquired, you can ensure that only one instance of your application is running.
The Code
This example uses a Mutex named MutexExample to ensure that only a single instance of the example can execute:
using System;
using System.Threading;
namespace Apress.VisualCSharpRecipes.Chapter04
{
class Recipe04_17
{
public static void Main()
{
C H A P T E R 4 ■ T H R E A D S, P R O C E S S E S, A N D S Y N C H R O N I Z AT I O N |
141 |
//A boolean that indicates whether this application has
//initial ownership of the Mutex.
bool ownsMutex;
//Attempt to create and take ownership of a Mutex named
//MutexExample.
using (Mutex mutex =
new Mutex(true, "MutexExample", out ownsMutex))
{
//If the application owns the Mutex it can continue to execute;
//otherwise, the application should exit.
if (ownsMutex)
{
Console.WriteLine("This application currently owns the" +
"mutex named MutexExample. Additional instances of" +
"this application will not run until you release" +
"the mutex by pressing Enter.");
Console.ReadLine();
// Release the mutex mutex.ReleaseMutex();
}
else
{
Console.WriteLine("Another instance of this application " +
"already owns the mutex named MutexExample. This" +
"instance of the application will terminate.");
}
}
// Wait to continue.
Console.WriteLine("Main method complete. Press Enter."); Console.ReadLine();
}
}
}
■Note If you do not construct the Mutex in a using statement and encapsulate the body of your application in the body of the using block as shown in this example, in long-running applications the garbage collector may dispose of the Mutex if it is not referenced after initial creation. This will result in releasing the Mutex and allow additional instances of the application to execute concurrently. In these circumstances, you should include the statement System.GC.KeepAlive(mutex) to ensure the Mutex is not garbage collected. Thanks to Michael A. Covington for highlighting this possibility.
C H A P T E R 5
■ ■ ■
Files, Directories, and I/O
The Microsoft .NET Framework I/O classes fall into two basic categories. First are the classes that retrieve information from the file system and allow you to perform file system operations such as copying files and moving directories. Two examples include the FileInfo and the DirectoryInfo classes. The second, and possibly more important, category includes a broad range of classes that allow you to read and write data from all types of streams. Streams can correspond to binary or text files, a file in an isolated store, a network connection, or even a memory buffer. In all cases, the way you interact with a stream is the same. This chapter describes how to use the file system classes and a wide range of stream-based classes.
The recipes in this chapter describe how to do the following:
•Retrieve or modify information about a file, directory, or a drive (recipes 5-1, 5-2, 5-4, 5-5, and 5-16)
•Copy, move, and delete files and directories (recipe 5-3)
•Show a directory tree in a Microsoft Windows-based application and use the common file dialog boxes (recipes 5-6 and 5-17)
•Read and write text and binary files (recipes 5-7 and 5-8)
•Create temporary files and files in a user-specific isolated store (recipes 5-15 and 5-18)
•Read files asynchronously (recipe 5-9)
•Search for specific files and test files for equality (recipes 5-10 and 5-11)
•Work with strings that contain path information (recipes 5-12, 5-13, and 5-14)
•Monitor the file system for changes (recipe 5-19)
•Write to a COM port (recipe 5-20)
•Generate a random filename (recipe 5-21)
•Retrieve or modify the access control lists (ACLs) of a file or directory (recipe 5-22)
5-1. Retrieve Information About a File, Directory, or Drive
Problem
You need to retrieve information about a file, directory, or drive.
143
144 C H A P T E R 5 ■ F I L E S, D I R E C TO R I E S, A N D I / O
Solution
Create a new System.IO.FileInfo, System.IO.DirectoryInfo, or System.IO.DriveInfo object, depending on the type of resource about which you need to retrieve information. Supply the path of the resource to the constructor, and then you will be able to retrieve information through the properties of the class.
How It Works
To create a FileInfo, DirectoryInfo, or DriveInfo object, you supply a relative or fully qualified path in the constructor. You can retrieve information through the corresponding object properties. Table 5-1 lists some of the key members that are found in these objects.
Table 5-1. Key Members for Files, Directories, and Drives
Member |
Applies To |
Description |
Exists |
FileInfo and |
Returns true or false, depending on whether |
|
DirectoryInfo |
a file or a directory exists at the specified |
|
|
location. |
Attributes |
FileInfo and |
Returns one or more values |
|
DirectoryInfo |
from the System.IO.FileAttributes |
|
|
enumeration, which represents the |
|
|
attributes of the file or the directory. |
CreationTime, LastAccessTime, |
FileInfo and |
Return System.DateTime |
and LastWriteTime |
DirectoryInfo |
instances that describe when |
|
|
a file or a directory was created, last |
|
|
accessed, and last updated, respectively. |
FullName, Name, and Extension |
FileInfo and |
Return a string that represents the fully |
|
DirectoryInfo |
qualified name, the directory, or the |
|
|
filename (with extension), and the |
|
|
extension on its own. |
IsReadOnly |
FileInfo |
Returns true or false, depending on |
|
|
whether a file is read-only. |
Length |
FileInfo |
Returns the file size as a number of bytes. |
DirectoryName and Directory |
FileInfo |
DirectoryName returns the name of the |
|
|
parent directory as a string. Directory |
|
|
returns a full DirectoryInfo object that |
|
|
represents the parent directory and allows |
|
|
you to retrieve more information about it. |
Parent and Root |
DirectoryInfo |
Return a DirectoryInfo object that |
|
|
represents the parent or root directory. |
CreateSubdirectory |
DirectoryInfo |
Creates a directory with the specified name |
|
|
in the directory represented by the |
|
|
DirectoryInfo object. It also returns a new |
|
|
DirectoryInfo object that represents the |
|
|
subdirectory. |
GetDirectories |
DirectoryInfo |
Returns an array of DirectoryInfo objects, |
|
|
with one element for each subdirectory |
|
|
contained in this directory. |
GetFiles |
DirectoryInfo |
Returns an array of FileInfo objects, with |
|
|
one element for each file contained in this |
|
|
directory. |
C H A P T E R 5 ■ F I L E S, D I R E C TO R I E S, A N D I / O |
145 |
Member |
Applies To |
Description |
DriveType |
DriveInfo |
Returns a DriveType enumeration value that |
|
|
represents the type of the specified drive; |
|
|
for example, Fixed or CD Rom. |
AvailableFreeSpace |
DriveInfo |
Returns a long that represents the free space |
|
|
available in the drive. |
GetDrives |
DriveInfo |
Returns an array of DriveInfo objects that |
|
|
represents the logical drives in the |
|
|
computer. |
|
|
|
The following are a few points to note while working with these objects:
•FileInfo and DirectoryInfo classes derive from the abstract FileSystemInfo class, which defines common methods like CreationTime, Exists, and so on. The DriveInfo class does not inherit from this base class, so it does not provide some of the common members available in the other two classes.
•The full set of properties FileInfo and DirectoryInfo objects expose is read the first time you interrogate any property. If the file or directory changes after this point, you must call the Refresh method to update the properties. However, this is not the case for DriveInfo; each property access asks the file system for an up-to-date value.
•You will not encounter an error if you specify a path that does not correspond to an existing file, directory, or drive. Instead, you will receive an object that represents an entity that does not exist—its Exists (or IsReady property for DriveInfo) property will be false. You can use this object to manipulate the entity. However, if you attempt to read most other properties, exceptions like FileNotFoundException, DirectoryNotFoundException, and so on will be thrown.
The Code
The following console application takes a file path from a command-line argument, and then displays information about the file, the containing directory, and the drive.
using System; using System.IO;
namespace Apress.VisualCSharpRecipes.Chapter05
{
static class Recipe05_01
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Please supply a filename."); return;
}
// Display file information.
FileInfo file = new FileInfo(args[0]);
Console.WriteLine("Checking file: " + file.Name);
Console.WriteLine("File exists: " + file.Exists.ToString());
146 C H A P T E R 5 ■ F I L E S, D I R E C TO R I E S, A N D I / O
if (file.Exists)
{
Console.Write("File created: "); Console.WriteLine(file.CreationTime.ToString()); Console.Write("File last updated: "); Console.WriteLine(file.LastWriteTime.ToString()); Console.Write("File last accessed: "); Console.WriteLine(file.LastAccessTime.ToString()); Console.Write("File size (bytes): "); Console.WriteLine(file.Length.ToString()); Console.Write("File attribute list: "); Console.WriteLine(file.Attributes.ToString());
}
Console.WriteLine();
// Display directory information. DirectoryInfo dir = file.Directory;
Console.WriteLine("Checking directory: " + dir.Name);
Console.WriteLine("In directory: " + dir.Parent.Name);
Console.Write("Directory exists: ");
Console.WriteLine(dir.Exists.ToString());
if (dir.Exists)
{
Console.Write("Directory created: "); Console.WriteLine(dir.CreationTime.ToString()); Console.Write("Directory last updated: "); Console.WriteLine(dir.LastWriteTime.ToString()); Console.Write("Directory last accessed: "); Console.WriteLine(dir.LastAccessTime.ToString()); Console.Write("Directory attribute list: "); Console.WriteLine(dir.Attributes.ToString()); Console.WriteLine("Directory contains: " +
dir.GetFiles().Length.ToString() + " files");
}
Console.WriteLine();
// Display drive information.
DriveInfo drv = new DriveInfo(file.FullName);
Console.Write("Drive: ");
Console.WriteLine(drv.Name);
if (drv.IsReady)
{
Console.Write("Drive type: "); Console.WriteLine(drv.DriveType.ToString()); Console.Write("Drive format: "); Console.WriteLine(drv.DriveFormat.ToString()); Console.Write("Drive free space: "); Console.WriteLine(drv.AvailableFreeSpace.ToString());
}
// Wait to continue. Console.WriteLine(Environment.NewLine); Console.WriteLine("Main method complete. Press Enter.");
C H A P T E R 5 ■ F I L E S, D I R E C TO R I E S, A N D I / O |
147 |
Console.ReadLine();
}
}
}
Usage
If you execute the command Recipe05-01.exe c:\windows\win.ini, you might expect the following output:
Checking file: win.ini
File exists: True
File created: 31.Mar.2003 5:30:00 PM
File last updated: 24.Sep.2005 11:11:13 PM
File last accessed: 10.Nov.2005 9:41:05 PM
File size (bytes): 658
File attribute list: Archive
Checking directory: windows
In directory: c:\
Directory exists: True
Directory created: 04.Jun.2005 4:47:56 PM
Directory last updated: 01.Nov.2005 10:09:45 AM
Directory last accessed: 11.Nov.2005 6:24:59 AM
Directory attribute list: Directory
Directory contains: 134 files
Drive: c:\
Drive type: Fixed
Drive format: NTFS
Drive free space: 14045097984
■Note Instead of using the instance methods of the FileInfo and DirectoryInfo classes, you can use the static File and Directory classes (note that a class corresponding to the DriveInfo class does not exist). The File and Directory methods expose most of the same functionality, but they require you to submit the filename or path with every method invocation. In cases where you need to perform multiple operations with the same file or directory, using the FileInfo and DirectoryInfo classes will be faster, because they will perform security checks only once. Also note that you could obtain the list of all logical drives in the computer by using the static
DriveInfo.GetDrives method.
5-2. Set File and Directory Attributes
Problem
You need to test or modify file or directory attributes.
Solution
Create a System.IO.FileInfo object for a file or a System.IO.DirectoryInfo object for a directory and use the bitwise AND (&) and OR (|) arithmetic operators to modify the value of the Attributes property.