Ratko Ćosić - lamentations of one programmer

četvrtak, 14.02.2008.

Application Recovery - finally Restart Manager

To continue with the story of the application recovery...

Main benefit, on my opinion, which has Restart Manager on previous approach is the ability of registering processes, files, and services relying on the application. That means, if our application shuts down, all related objects will try to shut down or restart, depending on a situation.

More information on that you can find at:
Application Shutdown Changes in Windows Vista

But now, let me just continue further...

RM uses the concept of the session, i.e. piece of isolation where everything related with the RM is handled. Sessions can exists 64 per user. So, we're starting with RmStartSession, closing the session with RmEndSession. With RmShutdown command we 'shut down' all linked resources, and with RmRestart we restart all linked resources.

Step 1:
Copy-paste of the PInvoke functions (this time from rstrtmgr.dll).

[StructLayout(LayoutKind.Sequential)]
struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public Com.FILETIME ProcessStartTime;
}

[Flags]
enum RM_SHUTDOWN_TYPE : uint
{
RmForceShutdown = 0x1,
RmShutdownOnlyRegistered = 0x10
}

delegate void RM_WRITE_STATUS_CALLBACK(UInt32 nPercentComplete);

[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out IntPtr pSessionHandle, int dwSessionFlags, string strSessionKey);

[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(IntPtr pSessionHandle);

[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmRegisterResources(IntPtr pSessionHandle, UInt32 nFiles, string[] rgsFilenames, UInt32 nApplications, RM_UNIQUE_PROCESS[] rgApplications, UInt32 nServices, string[] rgsServiceNames);

[DllImport("rstrtmgr.dll")]
static extern int RmShutdown(IntPtr pSessionHandle, RM_SHUTDOWN_TYPE lActionFlags, RM_WRITE_STATUS_CALLBACK fnStatus);

[DllImport("rstrtmgr.dll")]
static extern int RmRestart(IntPtr pSessionHandle, int dwRestartFlags, RM_WRITE_STATUS_CALLBACK fnStatus);

[DllImport("kernel32.dll")]
static extern bool GetProcessTimes(IntPtr hProcess, out Com.FILETIME lpCreationTime, out Com.FILETIME lpExitTime, out Com.FILETIME lpKernelTime, out Com.FILETIME lpUserTime);


Step 2:
What is following is a part of the code which does the job: creates RM session, registers dependent resources, shurts the resources and restarts them, and closes the session. The thing is trivial:

static void Main(string[] args)
{
IntPtr handle;
string key = Guid.NewGuid().ToString();

int res = RmStartSession(out handle, 0, key);
if (res == 0)
{
Console.WriteLine("Restart Manager session created with ID {0}", key);

RM_UNIQUE_PROCESS[] processes = GetProcesses("notepad");

res = RmRegisterResources(handle, // session handle
1, new string[] { @"Shared.xml" }, // number of shared files and list of shared files
(uint)processes.Length, processes, // number of dependant processes and list of processes
1, new string[] { "W3SVC" } ); // number of dependant services and list of services

if (res == 0)
{
Console.WriteLine("Successfully registered resources.");

res = RmShutdown(handle, RM_SHUTDOWN_TYPE.RmForceShutdown, ReportPercentage);
if (res == 0)
{
Console.WriteLine("Applications stopped successfully.n");

Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("Installing... ");
Thread.Sleep(5000);
Console.WriteLine("Done.n");
Console.ResetColor();

res = RmRestart(handle, 0, ReportPercentage);
if (res == 0)
Console.WriteLine("Applications restarted successfully.");
}
}
res = RmEndSession(handle);
if (res == 0)
Console.WriteLine("Restart Manager session ended.");

Console.ReadLine();

}
}


For the initialization of the session, I use new GUID and new pointer, which I later use also for calling another functions. By the registration of the resources, besides pointers of sessions, key-value pairs are required: number of files plus file list, number of processes plus process list, number of services plus services list. More over, there is a call of shutting down linked resources, and starting necessary resources. What I've found out is that if some of the resources is incorrectly stated, application fails or just continue to work malfunctionaly, and it doesn't react on RM events. Odd. So, be cautious.

Step 3:
For getting the information about the process, I use one, at first look, complex function which requires process by its name and returns an array of objects which complies RM in terms of processes (it requires additionally the date of starting the process), as I described in the following lines:


static RM_UNIQUE_PROCESS[] GetProcesses(string name)
{
List lst = new List();
foreach (Process p in Process.GetProcessesByName(name))
{
RM_UNIQUE_PROCESS rp = new RM_UNIQUE_PROCESS();
rp.dwProcessId = p.Id;
Com.FILETIME creationTime, exitTime, kernelTime, userTime;
GetProcessTimes(p.Handle, out creationTime, out exitTime, out kernelTime, out userTime);
rp.ProcessStartTime = creationTime;
lst.Add(rp);
}
return lst.ToArray();
}

static void ReportPercentage(UInt32 percent)
{
Console.WriteLine(percent);
}


So much about that, for now.

Continue with this topic

- 22:48 - Comments (0) - Print - #

<< Arhiva >>

Creative Commons License
Ovaj blog je ustupljen pod Creative Commons licencom Imenovanje-Nekomercijalno-Dijeli pod istim uvjetima.