Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,6 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/CompactGUI/My Project/launchSettings.json

# IntelliJ
.idea/
14 changes: 7 additions & 7 deletions CompactGUI.Core/Analyser.vb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Public Class Analyser
FolderName = folder
End Sub


<MeasurePerformance.IL.Weaver.MeasurePerformance>
Public Async Function AnalyseFolder(cancellationToken As CancellationToken) As Task(Of Boolean)
Dim allFiles = Await Task.Run(Function() Directory.EnumerateFiles(FolderName, "*", New EnumerationOptions() With {.RecurseSubdirectories = True, .IgnoreInaccessible = True}).AsShortPathNames, cancellationToken).ConfigureAwait(False)
Dim fileDetails As New List(Of AnalysedFileDetails)
Expand All @@ -33,7 +33,7 @@ Public Class Analyser
SyncLock fileDetails
fileDetails.Add(details)
End SyncLock
If details.CompressionMode <> CompressionAlgorithm.NO_COMPRESSION Then
If details.CompressionMode <> WOFCompressionAlgorithm.NO_COMPRESSION Then
Interlocked.Increment(compressedFilesCount)
End If
Interlocked.Add(localCompressedBytes, details.CompressedSize)
Expand Down Expand Up @@ -63,7 +63,7 @@ Public Class Analyser
If compSize < 0 Then
compSize = unCompSize ' GetFileSizeOnDisk failed, fall back to unCompSize
End If
Dim cLevel As CompressionAlgorithm = If(compSize = unCompSize, CompressionAlgorithm.NO_COMPRESSION, DetectCompression(fInfo))
Dim cLevel As WOFCompressionAlgorithm = If(compSize = unCompSize, WOFCompressionAlgorithm.NO_COMPRESSION, DetectCompression(fInfo))

Return New AnalysedFileDetails With {.FileName = file, .CompressedSize = compSize, .UncompressedSize = unCompSize, .CompressionMode = cLevel, .FileInfo = fInfo}
Catch ex As IOException
Expand All @@ -80,7 +80,7 @@ Public Class Analyser
Dim extRes As New Concurrent.ConcurrentDictionary(Of String, ExtensionResult)
Parallel.ForEach(FileCompressionDetailsList,
Sub(fl)
Dim xt = New IO.FileInfo(fl.FileName).Extension
Dim xt = New FileInfo(fl.FileName).Extension
If fl.UncompressedSize = 0 Then Return

extRes.AddOrUpdate(xt,
Expand Down Expand Up @@ -110,16 +110,16 @@ Public Class Analyser
End Function


Private Function DetectCompression(fInfo As FileInfo) As CompressionAlgorithm
Private Function DetectCompression(fInfo As FileInfo) As WOFCompressionAlgorithm

Dim isextFile As Integer
Dim prov As ULong
Dim info As WOF_FILE_COMPRESSION_INFO_V1
Dim buf As UInt16 = 8

Dim ret = WofIsExternalFile(fInfo.FullName, isextFile, prov, info, buf)
If isextFile = 0 Then info.Algorithm = CompressionAlgorithm.NO_COMPRESSION
If (fInfo.Attributes And 2048) <> 0 Then info.Algorithm = CompressionAlgorithm.LZNT1
If isextFile = 0 Then info.Algorithm = WOFCompressionAlgorithm.NO_COMPRESSION
If (fInfo.Attributes And 2048) <> 0 Then info.Algorithm = WOFCompressionAlgorithm.LZNT1
Return info.Algorithm

End Function
Expand Down
18 changes: 12 additions & 6 deletions CompactGUI.Core/Compactor.vb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Imports System.Threading

Public Class Compactor : Implements IDisposable

Public Sub New(folder As String, cLevel As CompressionAlgorithm, excludedFilesTypes As String())
Public Sub New(folder As String, cLevel As WOFCompressionAlgorithm, excludedFilesTypes As String())

If Not verifyFolder(folder).isValid Then Return

Expand All @@ -21,7 +21,7 @@ Public Class Compactor : Implements IDisposable

Private _workingDir As String
Private _excludedFileTypes() As String
Private _WOFCompressionLevel As CompressionAlgorithm
Private _WOFCompressionLevel As WOFCompressionAlgorithm

Private _EFInfo As WOF_FILE_COMPRESSION_INFO_V1
Private _EFInfoPtr As IntPtr
Expand All @@ -34,7 +34,7 @@ Public Class Compactor : Implements IDisposable



Public Async Function RunCompactAsync(Optional progressMonitor As IProgress(Of (percentageProgress As Integer, currentFile As String)) = Nothing, Optional MaxParallelism As Integer = 1) As Task(Of Boolean)
Public Async Function RunCompactAsync(Optional progressMonitor As IProgress(Of CompressionProgress) = Nothing, Optional MaxParallelism As Integer = 1) As Task(Of Boolean)
If _cancellationTokenSource.IsCancellationRequested Then Return False

Dim FilesList = Await BuildWorkingFilesList()
Expand All @@ -49,6 +49,12 @@ Public Class Compactor : Implements IDisposable

Dim paraOptions As New ParallelOptions With {.MaxDegreeOfParallelism = MaxParallelism}

'For Each file In FilesList
' Threading.Thread.Sleep(2000)
' Await PauseAndProcessFile(file.Item1, _cancellationTokenSource.Token, file.Item2, totalFilesSize, progressMonitor)
'Next


Await Parallel.ForEachAsync(FilesList, paraOptions,
Function(file, _ctx) As ValueTask
If _ctx.IsCancellationRequested Then Return ValueTask.FromCanceled(_ctx)
Expand All @@ -66,7 +72,7 @@ Public Class Compactor : Implements IDisposable
Return True
End Function

Private Async Function PauseAndProcessFile(file As String, _ctx As CancellationToken, fileSize As Long, totalFilesSize As Long, Optional progressMonitor As IProgress(Of (percentageProgress As Integer, currentFile As String)) = Nothing) As Task
Private Async Function PauseAndProcessFile(file As String, _ctx As CancellationToken, fileSize As Long, totalFilesSize As Long, Optional progressMonitor As IProgress(Of CompressionProgress) = Nothing) As Task

If _ctx.IsCancellationRequested Then Return
Try
Expand All @@ -84,7 +90,7 @@ Public Class Compactor : Implements IDisposable
Interlocked.Add(_processedFilesBytes, fileSize)
Dim incremented = _processedFilesBytes

progressMonitor?.Report((CInt(((incremented / totalFilesSize) * 100)), file))
progressMonitor?.Report(New CompressionProgress(CInt(((incremented / totalFilesSize) * 100)), file))

End Function

Expand Down Expand Up @@ -140,7 +146,7 @@ Public Class Compactor : Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
_cancellationTokenSource.Dispose()
_pauseSemaphore.Dispose()
If _EFInfoPtr <> IntPtr.Zero Then
If Not _EFInfoPtr.Equals(IntPtr.Zero) Then
Marshal.FreeHGlobal(_EFInfoPtr)
_EFInfoPtr = IntPtr.Zero
End If
Expand Down
31 changes: 7 additions & 24 deletions CompactGUI.Core/SharedMethods.vb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ Public Module SharedMethods

Function verifyFolder(folder As String) As (isValid As Boolean, msg As String)

If Not IO.Directory.Exists(folder) Then : Return (False, "Directory does not exist")
ElseIf folder.Contains((Environment.GetFolderPath(Environment.SpecialFolder.Windows))) Then : Return (False, "Cannot compress system directory")
If Not Directory.Exists(folder) Then : Return (False, "Directory does not exist")
ElseIf folder.ToLowerInvariant.Contains((Environment.GetFolderPath(Environment.SpecialFolder.Windows)).ToLowerInvariant) Then : Return (False, "Cannot compress system directory")
ElseIf folder.EndsWith(":\") Then : Return (False, "Cannot compress root directory")
ElseIf IsDirectoryEmptySafe(folder) Then : Return (False, "This directory is either empty or you are not authorized to access its files.")
ElseIf IsOneDriveFolder(folder) Then : Return (False, "Files synced with OneDrive cannot be compressed as they use a different storage structure")
Expand All @@ -22,27 +22,10 @@ Public Module SharedMethods
Function IsDirectoryEmptySafe(folder As String)

Try
Return Not IO.Directory.EnumerateFileSystemEntries(folder).Any()
Return Not Directory.EnumerateFileSystemEntries(folder).Any()

For Each subdir In IO.Directory.EnumerateDirectories(folder)
Try
If Not IsDirectoryEmptySafe(subdir) Then Return False
Catch ex As System.UnauthorizedAccessException

End Try
Next

For Each file In IO.Directory.EnumerateFiles(folder)
Try
Return False
Catch ex As System.UnauthorizedAccessException

End Try
Next

Return True

Catch ex As System.UnauthorizedAccessException
Catch ex As UnauthorizedAccessException
MsgBox("You are not authorized to access some items in this folder." & vbCrLf & "Please try running CompactGUI as an administrator, otherwise these items will be skipped.", MsgBoxStyle.Exclamation, "Unauthorized Access")
Return False

Expand Down Expand Up @@ -138,16 +121,16 @@ Public Module SharedMethods

<DllImport("kernel32.dll", CharSet:=CharSet.Auto)>
Private Function GetShortPathName(
<MarshalAs(UnmanagedType.LPTStr)> ByVal path As String,
<MarshalAs(UnmanagedType.LPTStr)> ByVal shortPath As StringBuilder, ByVal shortPathLength As Integer) As Integer
<MarshalAs(UnmanagedType.LPTStr)> path As String,
<MarshalAs(UnmanagedType.LPTStr)> shortPath As StringBuilder, shortPathLength As Integer) As Integer

End Function



<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Function GetDiskFreeSpace(
ByVal lpRootPathName As String,
lpRootPathName As String,
<Out> ByRef lpSectorsPerCluster As UInteger,
<Out> ByRef lpBytesPerSector As UInteger,
<Out> ByRef lpNumberOfFreeClusters As UInteger,
Expand Down
33 changes: 27 additions & 6 deletions CompactGUI.Core/SharedObjects.vb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Public Class AnalysedFileDetails

Public FileName As String
Public UncompressedSize As Long
Public CompressedSize As Long
Public CompressionMode As CompressionAlgorithm
Public FileInfo As IO.FileInfo
Public Property FileName As String
Public Property UncompressedSize As Long
Public Property CompressedSize As Long
Public Property CompressionMode As WOFCompressionAlgorithm
Public Property FileInfo As IO.FileInfo
End Class


Expand All @@ -23,7 +23,28 @@ Public Class ExtensionResult

End Class

Public Enum CompressionAlgorithm
Public Structure CompressionProgress
Public ProgressPercent As Integer
Public FileName As String

Public Sub New(_progressPercent As Integer, _fileName As String)
ProgressPercent = _progressPercent
FileName = _fileName
End Sub

End Structure



Public Enum CompressionMode
XPRESS4K
XPRESS8K
XPRESS16K
LZX
None
End Enum

Public Enum WOFCompressionAlgorithm
NO_COMPRESSION = -2
LZNT1 = -1
XPRESS4K = 0
Expand Down
6 changes: 3 additions & 3 deletions CompactGUI.Core/Uncompactor.vb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Public Class Uncompactor
Private _cancellationTokenSource As New CancellationTokenSource


Public Async Function UncompactFiles(filesList As List(Of String), Optional progressMonitor As IProgress(Of (percentageProgress As Integer, currentFile As String)) = Nothing, Optional MaxParallelism As Integer = 1) As Task(Of Boolean)
Public Async Function UncompactFiles(filesList As List(Of String), Optional progressMonitor As IProgress(Of CompressionProgress) = Nothing, Optional MaxParallelism As Integer = 1) As Task(Of Boolean)

Dim totalFiles As Integer = filesList.Count

Expand All @@ -36,7 +36,7 @@ Public Class Uncompactor
Return True
End Function

Private Async Function PauseAndProcessFile(file As String, _ctx As CancellationToken, totalFiles As Integer, progressMonitor As IProgress(Of (percentageProgress As Integer, currentFile As String))) As Task
Private Async Function PauseAndProcessFile(file As String, _ctx As CancellationToken, totalFiles As Integer, progressMonitor As IProgress(Of CompressionProgress)) As Task
If _ctx.IsCancellationRequested Then Return

Try
Expand All @@ -51,7 +51,7 @@ Public Class Uncompactor
Dim res = WOFDecompressFile(file)
_processedFileCount.TryAdd(file, 1)
Dim incremented = _processedFileCount.Count
progressMonitor?.Report((CInt(((incremented / totalFiles) * 100)), file))
progressMonitor?.Report(New CompressionProgress((CInt(((incremented / totalFiles) * 100))), file))
End Function

Private Function WOFDecompressFile(path As String)
Expand Down
34 changes: 22 additions & 12 deletions CompactGUI.Core/WOFHelper.vb
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,47 @@ Public Module WOFHelper
Public Function WOFConvertCompressionLevel(compressionlevel As Integer) As Integer

Select Case compressionlevel
Case 0 : Return CompressionAlgorithm.XPRESS4K
Case 1 : Return CompressionAlgorithm.XPRESS8K
Case 2 : Return CompressionAlgorithm.XPRESS16K
Case 3 : Return CompressionAlgorithm.LZX
Case Else : Return CompressionAlgorithm.XPRESS4K
Case 0 : Return WOFCompressionAlgorithm.XPRESS4K
Case 1 : Return WOFCompressionAlgorithm.XPRESS8K
Case 2 : Return WOFCompressionAlgorithm.XPRESS16K
Case 3 : Return WOFCompressionAlgorithm.LZX
Case Else : Return WOFCompressionAlgorithm.XPRESS4K
End Select

End Function

Public Function WOFConvertCompressionLevel(compressionlevel As CompressionMode) As Integer
Select Case compressionlevel
Case CompressionMode.XPRESS4K : Return WOFCompressionAlgorithm.XPRESS4K
Case CompressionMode.XPRESS8K : Return WOFCompressionAlgorithm.XPRESS8K
Case CompressionMode.XPRESS16K : Return WOFCompressionAlgorithm.XPRESS16K
Case CompressionMode.LZX : Return WOFCompressionAlgorithm.LZX
Case Else : Return WOFCompressionAlgorithm.XPRESS4K
End Select
End Function


Public Function WOFConvertBackCompressionLevel(WOFCompressionLevel As CompressionAlgorithm) As Integer
Public Function WOFConvertBackCompressionLevel(WOFCompressionLevel As WOFCompressionAlgorithm) As Integer

Select Case WOFCompressionLevel
Case CompressionAlgorithm.XPRESS4K : Return 0
Case CompressionAlgorithm.XPRESS8K : Return 1
Case CompressionAlgorithm.XPRESS16K : Return 2
Case CompressionAlgorithm.LZX : Return 3
Case WOFCompressionAlgorithm.XPRESS4K : Return 0
Case WOFCompressionAlgorithm.XPRESS8K : Return 1
Case WOFCompressionAlgorithm.XPRESS16K : Return 2
Case WOFCompressionAlgorithm.LZX : Return 3
Case Else : Return 0
End Select

End Function

Public Structure WOF_FILE_COMPRESSION_INFO_V1
Public Algorithm As CompressionAlgorithm
Public Algorithm As WOFCompressionAlgorithm
Public Flags As ULong
End Structure


<DllImport("WofUtil.dll")>
Friend Function WofIsExternalFile(
<MarshalAs(UnmanagedType.LPWStr)> ByVal Filepath As String,
<MarshalAs(UnmanagedType.LPWStr)> Filepath As String,
<Out> ByRef IsExternalFile As Integer,
<Out> ByRef Provider As UInteger,
<Out> ByRef Info As WOF_FILE_COMPRESSION_INFO_V1,
Expand Down
7 changes: 3 additions & 4 deletions CompactGUI.Watcher/BackgroundCompactor.vb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Imports System.Collections.ObjectModel
Imports System.Runtime
Imports System.Threading

Public Class BackgroundCompactor
Expand Down Expand Up @@ -47,9 +46,9 @@ Public Class BackgroundCompactor
End If
End Sub

Public Function BeginCompacting(folder As String, compressionLevel As Core.CompressionAlgorithm) As Task(Of Boolean)
Public Function BeginCompacting(folder As String, compressionLevel As Core.WOFCompressionAlgorithm) As Task(Of Boolean)

If compressionLevel = Core.CompressionAlgorithm.NO_COMPRESSION Then Return Task.FromResult(False)
If compressionLevel = Core.WOFCompressionAlgorithm.NO_COMPRESSION Then Return Task.FromResult(False)

_compactor = New Core.Compactor(folder, compressionLevel, _excludedFileTypes)

Expand All @@ -66,7 +65,7 @@ Public Class BackgroundCompactor
Dim currentProcess As Process = Process.GetCurrentProcess()
currentProcess.PriorityClass = ProcessPriorityClass.Idle

Dim foldersCopy As List(Of WatchedFolder) = folders.Where(Function(f) f.DecayPercentage <> 0 AndAlso f.CompressionLevel <> Core.CompressionAlgorithm.NO_COMPRESSION).ToList()
Dim foldersCopy As List(Of WatchedFolder) = folders.Where(Function(f) f.DecayPercentage <> 0 AndAlso f.CompressionLevel <> Core.WOFCompressionAlgorithm.NO_COMPRESSION).ToList()

Dim monitorsCopy As List(Of FolderMonitor) = monitors.ToList()

Expand Down
4 changes: 2 additions & 2 deletions CompactGUI.Watcher/IdleDetector.vb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Public Class IdleDetector
Public Shared Property IsEnabled As Boolean = True

Shared Sub New()
_idletimer = New PeriodicTimer(TimeSpan.FromSeconds(2))
_idletimer = New PeriodicTimer(TimeSpan.FromSeconds(5))
End Sub

Public Shared Sub Start()
Expand All @@ -32,7 +32,7 @@ Public Class IdleDetector
Private Shared Async Function IdleTimerDoWorkAsync() As Task

Try
While Await IdleDetector._idletimer.WaitForNextTickAsync(_cts.Token) AndAlso Not _cts.Token.IsCancellationRequested
While Await _idletimer.WaitForNextTickAsync(_cts.Token) AndAlso Not _cts.Token.IsCancellationRequested

If GetIdleTime() > 300 AndAlso Not Paused AndAlso IsEnabled Then
RaiseEvent IsIdle(Nothing, EventArgs.Empty)
Expand Down
8 changes: 4 additions & 4 deletions CompactGUI.Watcher/WatchedFolder.vb
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ Public Class WatchedFolder : Inherits ObservableObject
Public Property LastSystemModifiedDate As DateTime
Public Property LastCheckedDate As DateTime
Public Property LastCheckedSize As Long
Public Property CompressionLevel As Core.CompressionAlgorithm
Public Property CompressionLevel As Core.WOFCompressionAlgorithm
<JsonIgnore>
Public Property IsWorking As Boolean
Public ReadOnly Property DecayPercentage As Decimal
Get
If LastCompressedSize = 0 Then Return 1
Return If(LastUncompressedSize = LastCompressedSize OrElse LastCompressedSize > LastUncompressedSize, 0D, Math.Clamp((LastCheckedSize - LastCompressedSize) / (LastUncompressedSize - LastCompressedSize), 0, 1))
Return If(LastUncompressedSize = LastCompressedSize OrElse LastCompressedSize > LastUncompressedSize, 1D, Math.Clamp((LastCheckedSize - LastCompressedSize) / (LastUncompressedSize - LastCompressedSize), 0, 1))
End Get
End Property
<JsonIgnore>
Public ReadOnly Property SavedSpace As Long
Get
Return LastUncompressedSize - LastCompressedSize
Return LastUncompressedSize - LastCheckedSize
End Get
End Property

Public Sub RefreshProperties()
For Each prop In Me.GetType.GetProperties
Me.OnPropertyChanged(prop.Name)
OnPropertyChanged(prop.Name)
Next
End Sub

Expand Down
Loading