diff --git a/LockChecker/LockCheck.vb b/LockChecker/LockCheck.vb new file mode 100644 index 0000000..bce827f --- /dev/null +++ b/LockChecker/LockCheck.vb @@ -0,0 +1,233 @@ +' code sample taken from: +'https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-170ed5f3/sourcecode?fileId=151114&pathId=1558127374 +' Modified to suit the needs of this application +Imports System.ComponentModel +Imports System.Runtime.InteropServices + +Module LockCheck + ' maximum character count of application friendly name. + Private Const CCH_RM_MAX_APP_NAME As Integer = 255 + ' maximum character count of service short name. + Private Const CCH_RM_MAX_SVC_NAME As Integer = 63 + ' A system restart is not required. + Private Const RmRebootReasonNone As Integer = 0 + + ''' + ''' Uniquely identifies a process by its PID and the time the process began. + ''' An array of RM_UNIQUE_PROCESS structures can be passed + ''' to the RmRegisterResources function. + ''' + + Private Structure RM_UNIQUE_PROCESS + ' The product identifier (PID). + Public dwProcessId As Integer + ' The creation time of the process. + Public ProcessStartTime As System.Runtime.InteropServices.ComTypes.FILETIME + End Structure + + ''' + ''' Specifies the type of application that is described by + ''' the RM_PROCESS_INFO structure. + ''' + Private Enum RM_APP_TYPE + ' The application cannot be classified as any other type. + RmUnknownApp = 0 + ' A Windows application run as a stand-alone process that + ' displays a top-level window. + RmMainWindow = 1 + ' A Windows application that does not run as a stand-alone + ' process and does not display a top-level window. + RmOtherWindow = 2 + ' The application is a Windows service. + RmService = 3 + ' The application is Windows Explorer. + RmExplorer = 4 + ' The application is a stand-alone console application. + RmConsole = 5 + ' A system restart is required to complete the installation because + ' a process cannot be shut down. + RmCritical = 1000 + End Enum + + ''' + ''' Describes an application that is to be registered with the Restart Manager. + ''' + + Private Structure RM_PROCESS_INFO + ' Contains an RM_UNIQUE_PROCESS structure that uniquely identifies the + ' application by its PID and the time the process began. + Public Process As RM_UNIQUE_PROCESS + ' If the process is a service, this parameter returns the + ' long name for the service. + + Public strAppName As String + ' If the process is a service, this is the short name for the service. + + Public strServiceShortName As String + ' Contains an RM_APP_TYPE enumeration value. + Public ApplicationType As RM_APP_TYPE + ' Contains a bit mask that describes the current status of the application. + Public AppStatus As UInteger + ' Contains the Terminal Services session ID of the process. + Public TSSessionId As UInteger + ' TRUE if the application can be restarted by the + ' Restart Manager; otherwise, FALSE. + + Public bRestartable As Boolean + End Structure + + ''' + ''' Registers resources to a Restart Manager session. The Restart Manager uses + ''' the list of resources registered with the session to determine which + ''' applications and services must be shut down and restarted. Resources can be + ''' identified by filenames, service short names, or RM_UNIQUE_PROCESS structures + ''' that describe running applications. + ''' + ''' + ''' A handle to an existing Restart Manager session. + ''' + ''' The number of files being registered + ''' + ''' An array of null-terminated strings of full filename paths. + ''' + ''' The number of processes being registered + ''' An array of RM_UNIQUE_PROCESS structures + ''' The number of services to be registered + ''' + ''' An array of null-terminated strings of service short names. + ''' + ''' The function can return one of the system error codes that + ''' are defined in Winerror.h + ''' + + Private Function RmRegisterResources(pSessionHandle As UInteger, nFiles As UInt32, rgsFilenames As String(), nApplications As UInt32, <[In]> rgApplications As RM_UNIQUE_PROCESS(), nServices As UInt32, + rgsServiceNames As String()) As Integer + End Function + + ''' + ''' Starts a new Restart Manager session. A maximum of 64 Restart Manager + ''' sessions per user session can be open on the system at the same time. + ''' When this function starts a session, it returns a session handle and + ''' session key that can be used in subsequent calls to the Restart Manager API. + ''' + ''' + ''' A pointer to the handle of a Restart Manager session. + ''' + ''' Reserved. This parameter should be 0. + ''' + ''' A null-terminated string that contains the session key to the new session. + ''' + ''' + + Private Function RmStartSession(ByRef pSessionHandle As UInteger, dwSessionFlags As Integer, strSessionKey As String) As Integer + End Function + + ''' + ''' Ends the Restart Manager session. This function should be called by the + ''' primary installer that has previously started the session by calling the + ''' RmStartSession function. The RmEndSession function can be called by a + ''' secondary installer that is joined to the session once no more resources + ''' need to be registered by the secondary installer. + ''' + ''' + ''' A handle to an existing Restart Manager session. + ''' + ''' + ''' The function can return one of the system error codes + ''' that are defined in Winerror.h. + ''' + + Private Function RmEndSession(pSessionHandle As UInteger) As Integer + End Function + + ''' + ''' Gets a list of all applications and services that are currently using + ''' resources that have been registered with the Restart Manager session. + ''' + ''' + ''' A handle to an existing Restart Manager session. + ''' + ''' A pointer to an array size necessary to + ''' receive RM_PROCESS_INFO structures required to return information for + ''' all affected applications and services. + ''' + ''' + ''' A pointer to the total number of RM_PROCESS_INFO structures in an array + ''' and number of structures filled. + ''' + ''' + ''' An array of RM_PROCESS_INFO structures that list the applications and + ''' services using resources that have been registered with the session. + ''' + ''' + ''' Pointer to location that receives a value of the RM_REBOOT_REASON + ''' enumeration that describes the reason a system restart is needed. + ''' + ''' + + Private Function RmGetList(dwSessionHandle As UInteger, ByRef pnProcInfoNeeded As UInteger, ByRef pnProcInfo As UInteger, <[In], Out> rgAffectedApps As RM_PROCESS_INFO(), ByRef lpdwRebootReasons As UInteger) As Integer + End Function + + Function LockCheck(Filename As String) + Dim handle As UInteger + Dim sessionkey As String = Guid.NewGuid().ToString() + Dim processes As New List(Of Process)() + Dim result As String = "" + + Dim res As Integer = RmStartSession(handle, 0, sessionkey) + If res <> 0 Then + Throw New Exception("Could not begin restart session.") + End If + + Try + Dim pnProcInfoNeeded As UInteger = 0, pnProcInfo As UInteger = 100, lpdwRebootReasons As UInteger = RmRebootReasonNone + Dim resources As String() = New String() {Filename} + + ' Create an array to store the process results. + Dim processInfo As RM_PROCESS_INFO() = New RM_PROCESS_INFO(pnProcInfo - 1) {} + + res = RmRegisterResources(handle, CUInt(resources.Length), resources, 0, Nothing, 0, + Nothing) + If res <> 0 Then + Throw New Exception("Could not register resource.") + End If + + res = RmGetList(handle, pnProcInfoNeeded, pnProcInfo, processInfo, lpdwRebootReasons) + If res = 0 Then + 'The function completed successfully. + + If pnProcInfo <> 0 Then + For i As Integer = 0 To pnProcInfo - 1 + 'Console.WriteLine("File Name :" + resources(0)) + result = result + "File Name :" + resources(0) + vbNewLine + 'Console.WriteLine("Application locking the file :" + processInfo(i).strAppName) + result = result + "Application locking the file : " + processInfo(i).strAppName + vbNewLine + result = result + "PID of process locking the file: " + processInfo(i).Process.dwProcessId.ToString() + vbNewLine + Next + Else + 'Console.WriteLine("The specified file '{0}' is not locked by any process", resources(0)) + result = result + "No locks" + + End If + Else + Throw New Exception("Could not list processes locking resource.") + End If + + If res <> 0 Then + Throw New Win32Exception(Marshal.GetLastWin32Error()) + + End If + Catch exception As Exception + 'Console.WriteLine(exception.Message) + result = result + exception.Message + Finally + RmEndSession(handle) + End Try + Return result + End Function +End Module +Public Class LockChecker + Public Function CheckLock(FileName As String) + Return LockCheck.LockCheck(FileName) + End Function +End Class \ No newline at end of file diff --git a/LockChecker/LockChecker.vbproj b/LockChecker/LockChecker.vbproj new file mode 100644 index 0000000..d9eb626 --- /dev/null +++ b/LockChecker/LockChecker.vbproj @@ -0,0 +1,102 @@ + + + + + Debug + AnyCPU + {029C010D-728B-4B87-B54A-08B2BBF49BD7} + Library + LockChecker + LockChecker + 512 + Windows + v4.0 + true + + + true + full + true + true + bin\Debug\ + LockChecker.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + LockChecker.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + On + + + Binary + + + Off + + + On + + + + + + + + + + + + + + + + + + + + + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + \ No newline at end of file diff --git a/LockChecker/My Project/Application.Designer.vb b/LockChecker/My Project/Application.Designer.vb new file mode 100644 index 0000000..88dd01c --- /dev/null +++ b/LockChecker/My Project/Application.Designer.vb @@ -0,0 +1,13 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + diff --git a/LockChecker/My Project/Application.myapp b/LockChecker/My Project/Application.myapp new file mode 100644 index 0000000..758895d --- /dev/null +++ b/LockChecker/My Project/Application.myapp @@ -0,0 +1,10 @@ + + + false + false + 0 + true + 0 + 1 + true + diff --git a/LockChecker/My Project/AssemblyInfo.vb b/LockChecker/My Project/AssemblyInfo.vb new file mode 100644 index 0000000..8e94d0f --- /dev/null +++ b/LockChecker/My Project/AssemblyInfo.vb @@ -0,0 +1,35 @@ +Imports System +Imports System.Reflection +Imports System.Runtime.InteropServices + +' General Information about an assembly is controlled through the following +' set of attributes. Change these attribute values to modify the information +' associated with an assembly. + +' Review the values of the assembly attributes + + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' + + + diff --git a/LockChecker/My Project/Resources.Designer.vb b/LockChecker/My Project/Resources.Designer.vb new file mode 100644 index 0000000..b8f458c --- /dev/null +++ b/LockChecker/My Project/Resources.Designer.vb @@ -0,0 +1,62 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ''' + ''' A strongly-typed resource class, for looking up localized strings, etc. + ''' + _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ''' + ''' Returns the cached ResourceManager instance used by this class. + ''' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("LockChecker.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ''' + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. + ''' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set(ByVal value As Global.System.Globalization.CultureInfo) + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/LockChecker/My Project/Resources.resx b/LockChecker/My Project/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/LockChecker/My Project/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/LockChecker/My Project/Settings.Designer.vb b/LockChecker/My Project/Settings.Designer.vb new file mode 100644 index 0000000..550a489 --- /dev/null +++ b/LockChecker/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.LockChecker.My.MySettings + Get + Return Global.LockChecker.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/LockChecker/My Project/Settings.settings b/LockChecker/My Project/Settings.settings new file mode 100644 index 0000000..85b890b --- /dev/null +++ b/LockChecker/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/RDP2MSILib/RDP2MSILib.vb b/RDP2MSILib/RDP2MSILib.vb index d026bd7..408033d 100644 --- a/RDP2MSILib/RDP2MSILib.vb +++ b/RDP2MSILib/RDP2MSILib.vb @@ -1,5 +1,5 @@ Imports System.Reflection.Assembly - +Imports System.Windows.Forms Public Class RDP Private rdpFilePath As String @@ -67,16 +67,45 @@ Public Class RDP 'Define temp files to delete Dim FilesToDelete As List(Of String) = New List(Of String)(New String() {wxsPath, wixobjPath, wixpdbPath}) - + Dim LockCheck As New LockChecker.LockChecker() + Dim FileLocked As String + Dim SkipFile As Boolean = False 'Check if rdp file is already in TEMP folder If rdpParentFolder = TempPath Then rdpInTemp = True 'if RDP file not in temp, copy to temp If Not rdpInTemp Then - My.Computer.FileSystem.CopyFile(rdpFilePath, rdpTempPath, True) + FileLocked = LockCheck.CheckLock(rdpTempPath) + While Not (FileLocked = "No locks") + If (MessageBox.Show("The file " + rdpTempPath + " is currently locked. Lock information:" + FileLocked + vbNewLine + "Do you want to try again?", "File Locked", MessageBoxButtons.YesNo) = DialogResult.Yes) Then + FileLocked = LockCheck.CheckLock(rdpTempPath) + Else + MessageBox.Show("The following file will not be deleted:" + vbNewLine + rdpTempPath) + SkipFile = True + FileLocked = "No locks" + End If + End While + If Not (SkipFile) Then + My.Computer.FileSystem.CopyFile(rdpFilePath, rdpTempPath, True) + End If + FilesToDelete.Add(rdpTempPath) If hasIcon Then - My.Computer.FileSystem.CopyFile(IconFilePath, icoTempPath, True) + FileLocked = LockCheck.CheckLock(icoTempPath) + While Not (FileLocked = "No locks") + If (MessageBox.Show("The file " + icoTempPath + " is currently locked. Lock information:" + FileLocked + vbNewLine + "Do you want to try again?", "File Locked", MessageBoxButtons.YesNo) = DialogResult.Yes) Then + FileLocked = LockCheck.CheckLock(icoTempPath) + Else + MessageBox.Show("The following file will not be deleted:" + vbNewLine + icoTempPath) + SkipFile = True + FileLocked = "No locks" + End If + End While + If Not (SkipFile) Then + + My.Computer.FileSystem.CopyFile(IconFilePath, icoTempPath, True) + End If + FilesToDelete.Add(icoTempPath) End If @@ -95,7 +124,21 @@ Public Class RDP 'If Not LightExitCode = 0 Then Exit Sub 'Move MSI file to destination and delete temp files - My.Computer.FileSystem.MoveFile(msiPath, DestinationPath, True) + FileLocked = LockCheck.CheckLock(DestinationPath) + While Not (FileLocked = "No locks") + If (MessageBox.Show("The file " + DestinationPath + " is currently locked. Lock information:" + FileLocked + vbNewLine + "Do you want to try again?", "File Locked", MessageBoxButtons.YesNo) = DialogResult.Yes) Then + FileLocked = LockCheck.CheckLock(DestinationPath) + Else + MessageBox.Show("The following file will not be deleted:" + vbNewLine + DestinationPath) + SkipFile = True + FileLocked = "No locks" + End If + End While + If Not (SkipFile) Then + + My.Computer.FileSystem.MoveFile(msiPath, DestinationPath, True) + End If + DeleteFiles(FilesToDelete) End Sub @@ -122,7 +165,22 @@ Public Class RDP Private Sub DeleteFiles(FilesArray As List(Of String)) For Each dFile In FilesArray - If My.Computer.FileSystem.FileExists(dFile) Then My.Computer.FileSystem.DeleteFile(dFile) + Dim LockCheck As New LockChecker.LockChecker() + Dim FileLocked As String + Dim SkipFile As Boolean = False + FileLocked = LockCheck.CheckLock(dFile) + While Not (FileLocked = "No locks") + If (MessageBox.Show("The file " + dFile + " is currently locked. Lock information:" + FileLocked + vbNewLine + "Do you want to try again?", "File Locked", MessageBoxButtons.YesNo) = DialogResult.Yes) Then + FileLocked = LockCheck.CheckLock(dFile) + Else + MessageBox.Show("The following file will not be deleted:" + vbNewLine + dFile) + SkipFile = True + FileLocked = "No locks" + End If + End While + If Not (SkipFile) Then + If My.Computer.FileSystem.FileExists(dFile) Then My.Computer.FileSystem.DeleteFile(dFile) + End If Next End Sub diff --git a/RDP2MSILib/RDP2MSILib.vbproj b/RDP2MSILib/RDP2MSILib.vbproj index 0ed5219..c5a0025 100644 --- a/RDP2MSILib/RDP2MSILib.vbproj +++ b/RDP2MSILib/RDP2MSILib.vbproj @@ -46,6 +46,7 @@ + @@ -99,6 +100,12 @@ Settings.Designer.vb + + + {029c010d-728b-4b87-b54a-08b2bbf49bd7} + LockChecker + +