Virtual Developer Workshop: Containerized Development with Docker
Ever wondered how some applications are able to restart and just continue exactly where it left off? Well, I have. My curiosity knows no boundaries….
Today, you will learn how to make an RM Aware application. There is a lot of work, so let's get started.
First, the complicated stuff.
The term API means Application Programming Interface. Any good program exposes some sort of API that can be used in other applications. Windows is no different. You have to remember that, in running Windows, you are indirectly executing code exposed by the Windows Operating System.
A simple action such as opening a file executes the desired functionality needed for the file to be run. Copying and pasting is another example.
Then, you get the functions running in the background that you usually do not have to care about: keeping track of memory usage, obtaining a list of running programs. I can go on.
Where Are These APIs Stored?
The Windows system, or any system for that matter, is made up out of a bunch of files, called DLLs amongst others. The term DLL means Dynamic Link Library. These files are dynamically linked with the Operating System to run the associated program with it. The methods of the programs are locked away in these files, and these methods make your applications work.
Now, to get back on topic. We need to make use of the specific methods inside the specific Windows DLL files to assist in your RM Aware applications.
Here is more information regarding APIs.
Some applications require a restart after they have been updated. Some applications require an entire system restart after installation, even after an uninstallation. Restart Manager enables all applications and services to be shut down and restarted. Enabling Restart Manager files that are in use get freed allows installation operations to complete. Restart Manager shuts down applications and services only if the caller has permission to do so. Here is more information on Restart Manager Aware applications.
Let's create something, shall we?
Create a new Visual Basic Windows Forms Project. You do not have to add anything to the form; leave it as is. Add a New class and name it anything you like. I have named mine 'RM'. Add the following Namespaces to your 'RM' class:
Imports System Imports System.Security Imports System.Diagnostics Imports System.ComponentModel Imports System.Collections.Generic Imports System.Runtime.InteropServices Imports Microsoft.Win32.SafeHandles Imports System.Security.Permissions Imports System.Runtime.ConstrainedExecution Imports System.Text
Some of these namespaces may seem new to you, so more information on these namespaces is in the next sections.
The SafeHandles namespace provides common functionality supporting file and operating system handles.
The System.Security.Permissions namespace controls access to operations and resources based on policy.
The System.Runtime.ConstrainedExecution namespace defines a contract for reliability between the author of some code and the developers who take a dependency on that code.
Continuing to Code
Add the following API functions to your class:
Public Class RM <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _ Public Shared Function RegisterApplicationRestart _ (ByVal pszCommandline As String, _ ByVal dwFlags As Integer) As UInteger End Function Public Shared Function RunningVistaOrLater() As Boolean Return System.Environment.OSVersion.Version.Major > 5 End Function <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _ Friend Shared Function GetApplicationRestartSettings(ByVal hprocess _ As IntPtr, ByVal pwzCommandline As IntPtr, ByRef pcchsize As UInteger, _ ByVal pwzflag As IntPtr) As Integer End Function <DllImport("rstrtmgr.dll", CharSet:=CharSet.Auto)> _ Friend Shared Function RmStartSession(ByRef pSessionHandle As UInteger, _ ByVal dwSessionFlags As Integer, _ ByVal strSessionKey As StringBuilder) As Integer End Function <DllImport("rstrtmgr.dll", CharSet:=CharSet.Auto)> _ Friend Shared Function RmRegisterResources(ByVal pSessionHandle _ As UInteger, ByVal nFiles As Integer, ByVal rgsFilenames As String(), _ ByVal nApplications As Integer, ByVal rgApplications _ As RM_UNIQUE_PROCESS(), ByVal nServices As Integer, _ ByVal rgsServiceNames As String()) As Integer End Function <DllImport("rstrtmgr.dll", CharSet:=CharSet.Auto)> _ Friend Shared Function RmShutdown(ByVal pSessionHandle As UInteger, _ ByVal dwSessionFlags As UInteger, ByVal fnStatus As IntPtr) As Integer End Function <DllImport("rstrtmgr.dll", CharSet:=CharSet.Auto)> _ Friend Shared Function RmRestart(ByVal pSessionHandle As UInteger, _ ByVal dwRestartFlags As UInteger, ByVal fnStatus As IntPtr) As Integer End Function <DllImport("rstrtmgr.dll", CharSet:=CharSet.Auto)> _ Friend Shared Function RmEndSession(ByVal pSessionHandle _ As UInteger) As Integer End Function End Class
Here is some more information on these APIs:
Add a new class to your project and replace the created code with the following Structure:
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Diagnostics Imports System.Runtime.InteropServices.ComTypes <System.Runtime.InteropServices.StructLayout _ (System.Runtime.InteropServices.LayoutKind.Sequential)> _ Friend Structure RM_UNIQUE_PROCESS Private dwProcessId As UInteger Private ProcessStartTime As FILETIME Friend Sub New(ByVal pid As Integer) dwProcessId = CInt(pid) Dim p As Process = Process.GetProcessById(pid) Dim dummy1 As FILETIME Dim dummy2 As FILETIME Dim dummy3 As FILETIME RM_UNIQUE_PROCESS.GetProcessTimes(p.Handle, ProcessStartTime, _ dummy1, dummy2, dummy3) End Sub <System.Runtime.InteropServices.DllImport("kernel32.dll")> _ Private Shared Sub GetProcessTimes(ByVal hprocess As IntPtr, _ ByRef lpCreationTime As FILETIME, ByRef lpExitTime As FILETIME, _ ByRef lpkernaltime As FILETIME, ByRef lpuserTime As FILETIME) End Sub End Structure
Add the following code to your form to set things in motion:
Public Class frmRMAware Dim WM_QUERYENDSESSION As Int32 = 17 Dim ENDSESSION_CLOSEAPP As Int32 = 1 Dim WM_ENDSESSION As Int32 = 22 Private Sub frmRMAware_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If RM.RunningVistaOrLater Then RM.RegisterApplicationRestart("Test", 0) End If End Sub Protected Overloads Overrides Sub WndProc(ByRef msg _ As System.Windows.Forms.Message) MyBase.WndProc(msg) If msg.Msg = WM_QUERYENDSESSION Or msg.Msg = WM_ENDSESSION Then If msg.LParam.ToString = ENDSESSION_CLOSEAPP Then End If End If End Sub End Class
In the preceding code for frmRMAware, I have overridden the Load event to close and then restart the application.
The term overriding means to give a new function to an existing function, thus replacing the function's old methodology.
WindowProc (or window procedure) is a user-defined callback function that processes messages sent to a window, here is more information on it:
Now that you are better versed in using RM Aware applications, I'm sure you'll implement them in your coding. Cheers!