From 5e2c6d19b7e309e7e13db986a5f7c011f344ca4e Mon Sep 17 00:00:00 2001 From: IT Hit Date: Fri, 10 Jun 2022 01:38:58 +0300 Subject: [PATCH] v5.0.14943.0-Beta --- Common/Common.csproj | 2 +- Common/SynchronizationState.cs | 59 ------ README.md | 51 +---- .../Common/Core/Common.Windows.Core.csproj | 2 +- .../Common.Windows.VirtualDrive.csproj | 5 +- .../Filter/MsOfficeFilterHelper.cs | 3 + .../VirtualDrive/FullSync/FullSyncService.cs | 186 ------------------ .../{FullSync => }/ServerToClientSync.cs | 0 .../Common/VirtualDrive/VirtualEngineBase.cs | 16 +- Windows/VirtualDrive/README.md | 51 +---- .../VirtualDrive.ShellExtension.csproj | 2 +- Windows/VirtualDrive/VirtualDrive/Program.cs | 10 +- .../VirtualDrive/RemoteStorageMonitor.cs | 10 +- .../SparsePackage/appxmanifest.xml | 1 + .../VirtualDrive/VirtualDrive.csproj | 2 +- .../VirtualDrive/VirtualEngine.cs | 4 +- .../VirtualDrive/VirtualFileSystemItem.cs | 39 ++-- .../VirtualFileSystem/RemoteStorageMonitor.cs | 10 +- .../VirtualFileSystemItem.cs | 39 ++-- Windows/WebDAVDrive/README.md | 77 +------- .../WebDAVDrive.ShellExtension.csproj | 2 +- .../WebDAVDrive.UI/WebDAVDrive.UI.csproj | 14 +- .../WebDAVDrive.UI/WindowsTrayInterface.cs | 2 + Windows/WebDAVDrive/WebDAVDrive/Program.cs | 10 +- .../WebDAVDrive/RemoteStorageMonitor.cs | 15 +- .../SparsePackage/appxmanifest.xml | 1 + .../WebDAVDrive/WebDAVDrive/VirtualEngine.cs | 3 +- .../WebDAVDrive/VirtualFileSystemItem.cs | 25 +-- .../WebDAVDrive/WebDAVDrive.csproj | 2 +- macOS/README.md | 75 +------ 30 files changed, 113 insertions(+), 605 deletions(-) delete mode 100644 Common/SynchronizationState.cs delete mode 100644 Windows/Common/VirtualDrive/FullSync/FullSyncService.cs rename Windows/Common/VirtualDrive/{FullSync => }/ServerToClientSync.cs (100%) diff --git a/Common/Common.csproj b/Common/Common.csproj index 2a22d73..eb6364a 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -10,6 +10,6 @@ AnyCPU;x64 - + \ No newline at end of file diff --git a/Common/SynchronizationState.cs b/Common/SynchronizationState.cs deleted file mode 100644 index da52fbe..0000000 --- a/Common/SynchronizationState.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ITHit.FileSystem.Samples.Common -{ - /// - /// Synchronization state. - /// - public enum SynchronizationState - { - /// - /// Synchronization is enabled. - /// - Enabled, - - /// - /// Synchronizing data between user file system and remote storage. - /// - Synchronizing, - - /// - /// Waiting for the next synchronization. - /// - Idle, - - /// - /// Synchronization disabled. - /// - Disabled - } - - /// - /// Synchronization state change event argument. - /// - public class SynchEventArgs : EventArgs - { - /// - /// New state of the service. - /// - public SynchronizationState NewState; - - /// - /// Creates instance of this class. - /// - /// New synchronization state. - public SynchEventArgs(SynchronizationState newState) - { - this.NewState = newState; - } - } - - /// - /// Synchronization state change event delegate. - /// - /// instance firing this event. - /// New synchronization state. - public delegate void SyncronizationEvent(object sender, SynchEventArgs synchEventArgs); -} diff --git a/README.md b/README.md index 8297b53..46b134b 100644 --- a/README.md +++ b/README.md @@ -1,50 +1 @@ - -

User-mode File System Examples for Windows and macOS in .NET/C#

-
- - +˙ž \ No newline at end of file diff --git a/Windows/Common/Core/Common.Windows.Core.csproj b/Windows/Common/Core/Common.Windows.Core.csproj index f492889..f4283f3 100644 --- a/Windows/Common/Core/Common.Windows.Core.csproj +++ b/Windows/Common/Core/Common.Windows.Core.csproj @@ -20,7 +20,7 @@ - + \ No newline at end of file diff --git a/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj b/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj index 622fb7a..8ac4657 100644 --- a/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj +++ b/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj @@ -12,13 +12,12 @@ - - + - + diff --git a/Windows/Common/VirtualDrive/Filter/MsOfficeFilterHelper.cs b/Windows/Common/VirtualDrive/Filter/MsOfficeFilterHelper.cs index 26717a5..a0a89c7 100644 --- a/Windows/Common/VirtualDrive/Filter/MsOfficeFilterHelper.cs +++ b/Windows/Common/VirtualDrive/Filter/MsOfficeFilterHelper.cs @@ -10,6 +10,9 @@ namespace ITHit.FileSystem.Samples.Common.Windows /// Provides methods to detect Microsoft Office files that should not be synced to the remote storage, /// such as temporary files, Microsoft Office lock files and MS Office files opened (locked) for editing. /// + /// + /// https://support.microsoft.com/en-us/topic/description-of-how-word-creates-temporary-files-66b112fb-d2c0-8f40-a0be-70a367cc4c85 + /// internal class MsOfficeFilterHelper { /// diff --git a/Windows/Common/VirtualDrive/FullSync/FullSyncService.cs b/Windows/Common/VirtualDrive/FullSync/FullSyncService.cs deleted file mode 100644 index 779a3cc..0000000 --- a/Windows/Common/VirtualDrive/FullSync/FullSyncService.cs +++ /dev/null @@ -1,186 +0,0 @@ -using log4net; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -using ITHit.FileSystem; -using ITHit.FileSystem.Samples.Common; -using ITHit.FileSystem.Samples.Common.Windows; - - -namespace ITHit.FileSystem.Samples.Common.Windows -{ - /// - /// Performs a full synchronization between the user file system and the remote storage, recursively processing all folders. - /// - /// - /// This is a simple full synchronyzation example. - /// You can use this class in your project out of the box or replace it with a more advanced algorithm. - /// This sample does not do UFS->RS sync for moved and deleted items. All items moved and deleted in user file system while the app is not running are lost. - /// - public class FullSyncService : IDisposable - { - /// - /// Current synchronization state. - /// - public virtual SynchronizationState SyncState { get; private set; } = SynchronizationState.Disabled; - - /// - /// Event, fired when synchronization state changes. - /// - public event SyncronizationEvent SyncEvent; - - /// - /// Virtual drive instance to which this synchronization service belongs. - /// - private readonly VirtualEngineBase engine; - - /// - /// Logger. - /// - public readonly ILogger Logger; - - /// - /// Timer to start synchronization. - /// - private readonly System.Timers.Timer timer = null; - - /// - /// User file system root path. - /// - private readonly string userFileSystemRootPath; - - /// - /// Creates instance of this class. - /// - /// Synchronization interval in milliseconds. - /// User file system root path. - /// Logger. - internal FullSyncService(double syncIntervalMs, string userFileSystemRootPath, VirtualEngineBase engine, ILogger logger) - { - timer = new System.Timers.Timer(syncIntervalMs); - timer.Elapsed += Timer_ElapsedAsync; - this.userFileSystemRootPath = userFileSystemRootPath; - this.engine = engine; - this.Logger = logger.CreateLogger("Sync Service"); - } - - /// - /// Starts synchronization. - /// - public async Task StartAsync() - { - if (SyncState != SynchronizationState.Disabled) - { - return; - } - - // Do not start next synchronyzation automatically, wait until a previous synchronyzation is completed. - timer.AutoReset = false; - timer.Start(); - InvokeSyncEvent(SynchronizationState.Enabled); - Logger.LogMessage("Started", userFileSystemRootPath); - } - - /// - /// Stops synchronization. - /// - public async Task StopAsync() - { - if (SyncState == SynchronizationState.Disabled) - { - return; - } - - timer.Stop(); - InvokeSyncEvent(SynchronizationState.Disabled); - Logger.LogMessage("Stopped", userFileSystemRootPath); - } - - private void InvokeSyncEvent(SynchronizationState newState) - { - if ( (SyncState == SynchronizationState.Disabled) && (newState == SynchronizationState.Idle)) - { - return; - } - - SyncState = newState; - if (SyncEvent != null) - { - SyncEvent.Invoke(this, new SynchEventArgs(newState)); - } - } - - private async void Timer_ElapsedAsync(object sender, System.Timers.ElapsedEventArgs e) - { - try - { - InvokeSyncEvent(SynchronizationState.Synchronizing); - - // UFS -> RS. Synchronize all created/updated/moved/deleted from the user file system to the remote storage. - await engine.ProcessAsync(); - - // UFS <- RS. Recursivery synchronize all updated/created/deleted file and folders present in the user file system. - //await new ServerToClientSync(engine, Log).SyncronizeFolderAsync(userFileSystemRootPath); - - InvokeSyncEvent(SynchronizationState.Idle); - } - catch(Exception ex) - { - Logger.LogError(null, null, null, ex); - } - finally - { - // Wait and than start synchronyzation again. - timer.Start(); - } - } - - private void Error(object sender, ErrorEventArgs e) - { - Logger.LogError(null, null, null, e.GetException()); - } - - private bool disposedValue = false; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - timer.Stop(); - timer.Dispose(); - Logger.LogMessage($"Disposed"); - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~SyncService() - // { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - - } -} diff --git a/Windows/Common/VirtualDrive/FullSync/ServerToClientSync.cs b/Windows/Common/VirtualDrive/ServerToClientSync.cs similarity index 100% rename from Windows/Common/VirtualDrive/FullSync/ServerToClientSync.cs rename to Windows/Common/VirtualDrive/ServerToClientSync.cs diff --git a/Windows/Common/VirtualDrive/VirtualEngineBase.cs b/Windows/Common/VirtualDrive/VirtualEngineBase.cs index 4fcaedd..518a09f 100644 --- a/Windows/Common/VirtualDrive/VirtualEngineBase.cs +++ b/Windows/Common/VirtualDrive/VirtualEngineBase.cs @@ -20,12 +20,6 @@ public abstract class VirtualEngineBase : EngineWindows /// // public readonly RemoteStorageMonitor RemoteStorageMonitor; - /// - /// Full synchronization service. - /// In case any changes are lost (the file is blocked, app restart, lost connection, etc.) this service will sync all changes. - /// - public readonly FullSyncService SyncService; - /// /// Path to the icons folder. /// @@ -48,14 +42,12 @@ public abstract class VirtualEngineBase : EngineWindows /// /// Path to the remote storage root. /// Path to the icons folder. - /// Full synchronization interval in milliseconds. /// Logger. public VirtualEngineBase( string license, string userFileSystemRootPath, string remoteStorageRootPath, string iconsFolderPath, - double syncIntervalMs, LogFormatter logFormatter) : base(license, userFileSystemRootPath) { @@ -71,7 +63,6 @@ public VirtualEngineBase( Debug += logFormatter.LogDebug; //RemoteStorageMonitor = new RemoteStorageMonitor(remoteStorageRootPath, this, log4net); - SyncService = new FullSyncService(syncIntervalMs, userFileSystemRootPath, this, this.Logger); } /// @@ -113,15 +104,13 @@ public override async Task FilterAsync(OperationType operationType, string public override async Task StartAsync(bool processModified = true, CancellationToken cancellationToken = default) { await base.StartAsync(processModified, cancellationToken); - //RemoteStorageMonitor.Start(); - await SyncService.StartAsync(); + //RemoteStorageMonitor.Start(); } public override async Task StopAsync() { await base.StopAsync(); - //RemoteStorageMonitor.Stop(); - await SyncService.StopAsync(); + //RemoteStorageMonitor.Stop(); } /// @@ -144,7 +133,6 @@ protected override void Dispose(bool disposing) if (disposing) { //RemoteStorageMonitor.Dispose(); - SyncService.Dispose(); } // TODO: free unmanaged resources (unmanaged objects) and override finalizer diff --git a/Windows/VirtualDrive/README.md b/Windows/VirtualDrive/README.md index f62c147..46b134b 100644 --- a/Windows/VirtualDrive/README.md +++ b/Windows/VirtualDrive/README.md @@ -1,50 +1 @@ - -

Virtual Drive Sample in .NET, C#

-

This is a virtual drive implementation with thumbnail support, Microsoft Office and AutoCAD documents editing support, and automatic Microsoft Office/AutoCAD documents locking. It also demonstrates custom column support in Windows File Manager. To simulate the remote storage, this sample is using a folder in the local file system on the same machine. This sample supports all basic synchronization features provided by the Virtual File System sample: folders on-demand listing, files on-demand content loading, selective offline files support, hydration progress. The sample is written in C#/.NET.

-

You can download this sample and a trial license in the product download area as well as you can clone it from GitHub

-

This sample is provided with IT Hit User File System v3 Beta and later versions.

- -
    -
  • .NET Core 6.0 or later.
  • -
  • Microsoft Windows 10 Creators Update or later version.
  • -
  • NTFS file system.
  • -
- -

By default, the sample will use the \RemoteStorage\ folder, located under the project root, to simulate the remote storage file structure. It will mount the user file system under the %USERPROFILE%\VFS\ folder (typically C:\Users\<username>\VirtualDrive\).

-

To specify the folder that will be used for remote storage simulation edit the "RemoteStorageRootPath" parameter in appsettings.json. This could be either an absolute path or a path relative to the application root.

-

To specify the user file system folder edit the "UserFileSystemRootPath" parameter in appsettings.json.

- -

To run the example, you will need a valid IT Hit User File System Engine for .NET License. You can download the license in the product download area. Note that the Engine is fully functional with a trial license and does not have any limitations. The trial license is valid for one month and the engine will stop working after this. You can check the expiration date inside the license file. Download the license file and specify its content in the UserFileSystemLicense field in appsettings.json file. Set the license content directly as a value (NOT as a path to the license file). Do not forget to escape quotes: \":

-
"UserFileSystemLicense": "<?xml version=\"1.0\" encoding=\"utf-8\"?><License…
-

You can also run the sample without explicitly specifying a license for 5 days. In this case, the Engine will automatically request the trial license from the IT Hit website https://www.userfilesystem.com. Make sure it is accessible via firewalls if any. After 5 days the Engine will stop working. To extend the trial period you will need to download a license in a product download area and specify it in appsettings.json

- -

To run the sample open the project in Visual Studio and run the project in debug mode. When starting in the debug mode, it will automatically create a folder in which the virtual file system will reside, register the virtual drive with the platform and then open two instances of Windows File Manager, one of which will show a virtual drive and another a folder simulating remote storage. 

-

You can find more about running and stopping the sample as well as about basic synchronization features in the Virtual File System sample description. 

-

Packaging Project

-

This sample provides a Windows Application Packaging Project which allows deployment of your application to the Windows Store, simplifies COM components debugging (thumbnails handler and Windows Explorer context menu), as well as your application can be installed without admin privileges. The package can be also used for direct deployment to users.

-

To start the project with thumbnails and context menu support follow these steps:

-
    -
  1. Set the packaging project as your startup project.
  2. -
  3. Set the VirtualDrive project under the packaging project as an Entry Point.
  4. -
-

Run the project from Visual Studio. This will automatically register COM components as well as you can start debugging COM components without additional steps.

-

The packaging project will also perform an automatic cleanup on uninstall. Your sync root registration will be automatically unregistered, folders created by the application will be deleted as well as all COM components unregistered. 

-

Thumbnails Support

-

The Virtual Drive sample provides a separate project with thumbnail provider implementation in the COM object. It loads thumbnails from files located in the remote storage simulation folder and displays them in Windows Explorer. You will adapt this project to load thumbnails from your real remote storage.

-

The thumbnails provider is registered as an application extension by the packing project provided with the sample. To register thumbnails you will simply run the packaging project. You do NOT need to register the thumbnails handler using regsrv32 or any using any other COM registration technique.

-

Virtual Drive thumbnails support in Windows Explorer

-

The thumbnails COM is automatically unregistered on package uninstall, you do not need to manually unregister it.

-

Microsoft Office and AutoCAD Files Editing Support

-

Another major difference between the Virtual Drive sample and the Virtual File System sample is its support for Microsoft Office and AutoCAD document editing. This sample supports synchronization of the MS Office/AutoCAD documents, avoiding the creation of the temporary files in the remote storage as well as it does not rename or delete the document in your remote storage during MS Office/AutoCAD transactional save operation, preserving all data associated with a file in your remote storage.

-

This sample automatically locks the Microsoft Office and AutoCAD document in the remote storage when a document is being opened for editing and automatically unlocks the document when the file is closed. When the document is opened you will see the lock icon Lock icon in the Status column in Windows File Manager:

-

 Virtual Drive sample shows lock icon for Microsoft Office documents

-

The information about the lock (lock-token, etc.) is being saved on the client machine when the document is locked. When a document is modified on the client, all changes to the document are being saved on the local drive, without being sent to the remote storage. You will see the not In-Sync icon  being displayed when you modify and save the document. The updated document content is sent to the remote storage when the document is closed, together with the lock-token and eTag. 

-

This sample provides classes for storing and managing lock information received from your remote storage and a code template for supplying the lock-token with each update operation, Windows Explorer icons updates, and Windows Explorer custom properties update. Locks, eTags, and custom properties are being stored in the application data folder, under the path that corresponds to the mounted virtual drive. The path to the data folder typically looks like: C:\Users\<user>\AppData\Local\Virtual Drive\C\Users\<user>\VirtualDrive\ServerData\

-

Any temporary Microsoft Office and AutoCAD documents (~$docfile.docx, G57BURP.tmp, etc) are stored in the local file system only and are NOT synchronized to the server. Temporary Microsoft Office/AutoCAD documents are being automatically deleted by Microsoft Office/AutoCAD when the document editing is completed.

-

Window File Manger Custom Columns Support

-

This sample registers and displays custom columns in Windows File Manager. For demo purposes the Registrar class add ETag column as well as columns that show information about the lock: Lock Owner, Lock Scope, Lock Expires:

-

  Virtual Drive custom columns being displayed in Windows File Manger

-

 

-

Next Article:

-WebDAV Drive Sample in .NET, C# - +˙ž \ No newline at end of file diff --git a/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj b/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj index 2e2ed72..03f2074 100644 --- a/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj +++ b/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj @@ -19,7 +19,7 @@ - + diff --git a/Windows/VirtualDrive/VirtualDrive/Program.cs b/Windows/VirtualDrive/VirtualDrive/Program.cs index dc2a816..f70c4fe 100644 --- a/Windows/VirtualDrive/VirtualDrive/Program.cs +++ b/Windows/VirtualDrive/VirtualDrive/Program.cs @@ -75,9 +75,9 @@ static async Task Main(string[] args) Settings.UserFileSystemRootPath, Settings.RemoteStorageRootPath, Settings.IconsFolderPath, - Settings.SyncIntervalMs, logFormatter)) { + Engine.OutgoingSyncService.SyncIntervalMs = Settings.SyncIntervalMs; Engine.AutoLock = Settings.AutoLock; // Set the remote storage item ID for the root item. It will be passed to the IEngine.GetFileSystemItemAsync() @@ -131,18 +131,18 @@ private static async Task ProcessUserInputAsync() case ConsoleKey.S: // Start/stop full synchronization. - if (Engine.SyncService.SyncState == SynchronizationState.Disabled) + if (Engine.OutgoingSyncService.SyncState == SynchronizationState.Disabled) { if(Engine.State != EngineState.Running) { - Engine.SyncService.Logger.LogError("Failed to start. The Engine must be running."); + Engine.OutgoingSyncService.Logger.LogError("Failed to start. The Engine must be running."); break; } - await Engine.SyncService.StartAsync(); + await Engine.OutgoingSyncService.StartAsync(); } else { - await Engine.SyncService.StopAsync(); + await Engine.OutgoingSyncService.StopAsync(); } break; diff --git a/Windows/VirtualDrive/VirtualDrive/RemoteStorageMonitor.cs b/Windows/VirtualDrive/VirtualDrive/RemoteStorageMonitor.cs index 8a2e970..0c5c2be 100644 --- a/Windows/VirtualDrive/VirtualDrive/RemoteStorageMonitor.cs +++ b/Windows/VirtualDrive/VirtualDrive/RemoteStorageMonitor.cs @@ -1,15 +1,11 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using System.Linq; -using log4net; using ITHit.FileSystem; +using ITHit.FileSystem.Windows; using ITHit.FileSystem.Samples.Common.Windows; -using ITHit.FileSystem.Samples.Common; + namespace VirtualDrive { @@ -170,7 +166,7 @@ private async void ChangedAsync(object sender, FileSystemEventArgs e) catch (IOException ex) { // The file is blocked in the user file system. This is a normal behaviour. - Logger.LogMessage(ex.Message); + Logger.LogDebug(ex.Message); } catch (Exception ex) { diff --git a/Windows/VirtualDrive/VirtualDrive/SparsePackage/appxmanifest.xml b/Windows/VirtualDrive/VirtualDrive/SparsePackage/appxmanifest.xml index 8141d95..369216e 100644 --- a/Windows/VirtualDrive/VirtualDrive/SparsePackage/appxmanifest.xml +++ b/Windows/VirtualDrive/VirtualDrive/SparsePackage/appxmanifest.xml @@ -11,6 +11,7 @@ xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" IgnorableNamespaces="uap rescap uap10"> + - + diff --git a/Windows/VirtualDrive/VirtualDrive/VirtualEngine.cs b/Windows/VirtualDrive/VirtualDrive/VirtualEngine.cs index 5359275..1c31241 100644 --- a/Windows/VirtualDrive/VirtualDrive/VirtualEngine.cs +++ b/Windows/VirtualDrive/VirtualDrive/VirtualEngine.cs @@ -28,16 +28,14 @@ public class VirtualEngine : VirtualEngineBase /// /// Path to the remote storage root. /// Path to the icons folder. - /// Full synchronization interval in milliseconds. /// Logger. public VirtualEngine( string license, string userFileSystemRootPath, string remoteStorageRootPath, string iconsFolderPath, - double syncIntervalMs, LogFormatter logFormatter) - : base(license, userFileSystemRootPath, remoteStorageRootPath, iconsFolderPath, syncIntervalMs, logFormatter) + : base(license, userFileSystemRootPath, remoteStorageRootPath, iconsFolderPath, logFormatter) { RemoteStorageMonitor = new RemoteStorageMonitor(remoteStorageRootPath, this, this.Logger); } diff --git a/Windows/VirtualDrive/VirtualDrive/VirtualFileSystemItem.cs b/Windows/VirtualDrive/VirtualDrive/VirtualFileSystemItem.cs index 74111ce..7553a13 100644 --- a/Windows/VirtualDrive/VirtualDrive/VirtualFileSystemItem.cs +++ b/Windows/VirtualDrive/VirtualDrive/VirtualFileSystemItem.cs @@ -123,25 +123,36 @@ public async Task DeleteCompletionAsync(IOperationContext operationContext, IInS try { remoteStoragePath = Mapping.GetRemoteStoragePathById(RemoteStorageItemId); + + FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); + if (remoteStorageItem != null) + { + if (remoteStorageItem is FileInfo) + { + remoteStorageItem.Delete(); + } + else + { + (remoteStorageItem as DirectoryInfo).Delete(true); + } + Logger.LogMessage("Deleted in the remote storage succesefully", UserFileSystemPath, default, operationContext); + } } - catch (FileNotFoundException) + catch (UnauthorizedAccessException) + { + // We want the Engine to try deleting this file again at a later time. + resultContext.SetInSync = false; + Logger.LogError("Failed to delete item", UserFileSystemPath, default, null, operationContext); + } + catch (DirectoryNotFoundException) { // Windows Explorer may call delete more than one time on the same file/folder. - return; + Logger.LogMessage("Folder already deleted", UserFileSystemPath, default, operationContext); } - - FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); - if (remoteStorageItem != null) + catch (FileNotFoundException) { - if (remoteStorageItem is FileInfo) - { - remoteStorageItem.Delete(); - } - else - { - (remoteStorageItem as DirectoryInfo).Delete(true); - } - Logger.LogMessage("Deleted in the remote storage succesefully", UserFileSystemPath, default, operationContext); + // Windows Explorer may call delete more than one time on the same file/folder. + Logger.LogMessage("File already deleted", UserFileSystemPath, default, operationContext); } } diff --git a/Windows/VirtualFileSystem/RemoteStorageMonitor.cs b/Windows/VirtualFileSystem/RemoteStorageMonitor.cs index 87c7105..7d0517f 100644 --- a/Windows/VirtualFileSystem/RemoteStorageMonitor.cs +++ b/Windows/VirtualFileSystem/RemoteStorageMonitor.cs @@ -1,15 +1,11 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using System.Linq; -using log4net; using ITHit.FileSystem; +using ITHit.FileSystem.Windows; using ITHit.FileSystem.Samples.Common.Windows; -using ITHit.FileSystem.Samples.Common; + namespace VirtualFileSystem { @@ -168,7 +164,7 @@ private async void ChangedAsync(object sender, FileSystemEventArgs e) catch (IOException ex) { // The file is blocked in the user file system. This is a normal behaviour. - Logger.LogMessage(ex.Message); + Logger.LogDebug(ex.Message); } catch (Exception ex) { diff --git a/Windows/VirtualFileSystem/VirtualFileSystemItem.cs b/Windows/VirtualFileSystem/VirtualFileSystemItem.cs index 60ead49..f900522 100644 --- a/Windows/VirtualFileSystem/VirtualFileSystemItem.cs +++ b/Windows/VirtualFileSystem/VirtualFileSystemItem.cs @@ -114,25 +114,36 @@ public async Task DeleteCompletionAsync(IOperationContext operationContext = nul try { remoteStoragePath = Mapping.GetRemoteStoragePathById(RemoteStorageItemId); + + FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); + if (remoteStorageItem != null) + { + if (remoteStorageItem is FileInfo) + { + remoteStorageItem.Delete(); + } + else + { + (remoteStorageItem as DirectoryInfo).Delete(true); + } + Logger.LogMessage("Deleted item in remote storage succesefully", UserFileSystemPath, default, operationContext); + } + } + catch (UnauthorizedAccessException) + { + // We want the Engine to try deleting this file again at a later time. + resultContext.SetInSync = false; + Logger.LogError("Failed to delete item", UserFileSystemPath, default, null, operationContext); } - catch(FileNotFoundException) + catch (DirectoryNotFoundException) { // Windows Explorer may call delete more than one time on the same file/folder. - return; + Logger.LogMessage("Folder already deleted", UserFileSystemPath, default, operationContext); } - - FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath); - if (remoteStorageItem != null) + catch (FileNotFoundException) { - if (remoteStorageItem is FileInfo) - { - remoteStorageItem.Delete(); - } - else - { - (remoteStorageItem as DirectoryInfo).Delete(true); - } - Logger.LogMessage("Deleted item in remote storage succesefully", UserFileSystemPath, default, operationContext); + // Windows Explorer may call delete more than one time on the same file/folder. + Logger.LogMessage("File already deleted", UserFileSystemPath, default, operationContext); } } diff --git a/Windows/WebDAVDrive/README.md b/Windows/WebDAVDrive/README.md index 12558a6..46b134b 100644 --- a/Windows/WebDAVDrive/README.md +++ b/Windows/WebDAVDrive/README.md @@ -1,76 +1 @@ - -

WebDAV Drive Sample in .NET, C#

-

This sample implements a virtual file system that displays documents from a WebDAV server. You can edit documents, upload and download documents as well as manage folders structure using Windows File Manager. This sample provides automatic documents locking for Microsoft Office and AutoCAD documents. It supports synchronization, on-demand loading, selective offline files support, upload and download progress. It synchronizes files and folders both from a WebDAV server to the local user file system and from the local user file system to the WebDAV server. This sample is written in .NET Core, C#.  

-

You can use this sample out-of-the-box to manage documents on a WebDAV server, or you can use it as a starting point for your custom virtual drive to create OneDrive-like features for your DMS/CRM/ERP and reprogram it to publish data from your storage. 

-

This sample is supplied as part of the SDK with IT Hit WebDAV Client Library for .NET and with IT Hit User File System.

-

You can download this sample and trial licenses in the IT Hit WebDAV Client Library product download area and in the IT Hit User File System product download area. You can also clone it or browse the code on GitHub

-

Requirements

-
    -
  • .NET Core 5.0 or later.
  • -
  • Microsoft Windows 10 Creators Update or later version.
  • -
  • NTFS file system.
  • -
  • The sample supports WebDAV servers with cookies authentication, Basic, Digest, NTLM, and Kerberos authentication.
  • -
  • To connect to WebDAV servers with cookies authentication Microsoft Edge is required. 
  • -
-

Configuring the Sample

-

To specify the WebDAV server URL edit the "WebDAVServerUrl" parameter in appsettings.json. This could be either a server root path (https://server/) or a WebDAV folder on your server (https://server/dav/).

-

For testing and demo purposes you can use one of the IT Hit demo servers. Navigate to https://webdavserver.net or // https://webdavserver.com in a web browser. Copy the URL or your test folder, that looks like https://webdavserver.net/User123456/ and specify it in the WebDAVServerUrl parameter.

-

By default, this sample will mount the user file system under the %USERPROFILE%\DAV\ folder (typically C:\Users\<username>\DAV\). To specify a different folder edit the "UserFileSystemRootPath" parameter in appsettings.json.

-

Setting the License

-

Note that to use the sample you need both the IT Hit WebDAV Client Library license and IT Hit User File System license.

-

To run the example, you will need both IT Hit WebDAV Client Library for .NET license and IT Hit User File System Engine for .NET License. You can download a trial license in the IT Hit WebDAV Client Library product download area and in the IT Hit User File System product download area. Note that this sample is fully functional with a trial license and does not have any limitations. The trial licenses are valid for one month will stop working after this. You can check the expiration date inside the license file. Download the licenses file and specify license strings in the WebDAVClientLicense and UserFileSystemLicense fields respectively in appsettings.json file. Set the license content directly as a value (NOT as a path to the license file). Do not forget to escape quotes: \":

-
"UserFileSystemLicense": "<?xml version=\"1.0\" encoding=\"utf-8\"?><License…
-

You can also run the sample without explicitly specifying a license for 5 days. In this case, the Engine will automatically request the trial licenses from the IT Hit website https://www.userfilesystem.com. Make sure it is accessible via firewalls if any. After 5 days the Engine will stop working. To extend the trial period you will need to download trial licenses and specify them in appsettings.json

-

Running the Sample

-

To run the sample open the project in Visual Studio and run the project in debug mode. In the debug mode this sample provides additional support for the development and testing convenience. When starting in the debug mode, it will automatically create a folder in which the virtual file system will reside, register the user file system with the platform and then open an instance of Windows File Manager with a mounted file system as well as will launch a default web browser navigating to the WebDAV server URL specified in your appsettings.json:

-

WebDAV Drive sample launches Windows File manager with mounted virtual file system displaying content of your WebDAV server

-

You can start managing and editing files on your WebDAV server and will see all changes being propagated to the file system on the client machine. You can also edit documents and manage file structure on the client and all changes will automatically be reflected on the WebDAV server.

- -

This sample provides a Windows Application Packaging Project which allows deployment of your application to the Windows Store, simplifies COM components debugging (thumbnails handler and Windows Explorer context menu), as well as your application can be installed without admin privileges. The package can be also used for direct deployment to users.

-

To start the project with thumbnails and context menu support follow these steps:

-
    -
  1. Set the packaging project as your startup project.
  2. -
  3. Set the VirtualDrive project under the packaging project as an Entry Point.
  4. -
-

Run the project from Visual Studio. This will automatically register COM components as well as you can start debugging COM components without additional steps.

-

The packaging project will also perform an automatic cleanup on uninstall. Your sync root registration will be automatically unregistered, folders created by the application will be deleted as well as all COM components unregistered. 

-

On-Demand Loading

-

Initially, when you start the application, the user file system does not contain any file of folder placeholders, except the sync root folder. The content of the folders is populated only when any application is listing folder content. The content of files is loaded only when an application is opening a file for reading or writing.

-

After running the sample all files and folders are marked with a cloud iconOffline File, which means that the content of this file or folder is not available locally but instead resides in the remote location. Even though the file shows the correct size in the Size column, the file occupies zero bytes on the disk. You can open the file Properties dialog to check the "Size on disk" value. You can see in the console log that only root folder files and folders placeholders are being created when Windows File Manager listed the root folder content during the initial launch. Folders located deeper in the hierarchy are not loaded until their content is being requested by the platform file system calls. 

-

When any application is accessing the file, located under the \DAV\ folder, opening it for reading or writing, the operating system redirects the call to this sample, which loads file content from the WebDAV server. The file becomes marked with a green check-mark on a white background iconLocal File, which means the file content is present on the local disk:

-

Local Cloud File. Green check-mark on a white background icon means the file content is present on the local disk.

-

The Windows File Manager provides the "Always keep on this device" and "Free up space" context menus, which are standard menus provided by Windows OS. If you select the "Always keep on this device" the file or entire folder structure will be recursively loaded to the local disk, all file content will be loaded to the local disk and will become available offline. All files and folders are marked with a pinned file iconPinned File. Pinned files will not be deleted from the drive even if it runs low on space.

-

Always keep on this device menu will load all files to a loacal disk and will keep them from purging on low disk space.

-

To remove content from the local disk select the "Free up space" menu. It will restore the cloud iconOffline File.

-

When a large file is being downloaded from the WebDAV server, this sample submits progress reports to the operation system, to show a standard Windows "Downloading" dialog. At the same time, the Windows File Manager also shows progress in the files list view:

-

Large file download from the remote storage show progress reports in Windows File Manager and Files View

-

Microsoft Office and AutoCAD Documents Editing

-

This sample supports synchronization of the Microsoft Office and AutoCAD documents, avoiding the creation of temporary files in the remote storage as well as it does not rename or delete the document in your remote storage during MS Office/AutoCAD transactional save operation, preserving all data associated with a file in your remote storage.

-

This sample automatically locks the Microsoft Office and AutoCAD document in the remote storage when a document is being opened for editing and automatically unlocks the document when the file is closed. When the document is opened you will see the lock icon Lock icon in the Status column in Windows File Manager:

-

WebDAV Drive automatically locks Microsoft office document when it is being opened for editing and unlocks when closed.

-

The information about the lock (lock-token, etc.) is being saved on the client machine. When a document is modified on the client, all changes to the document are being saved on the local drive, without being sent to the WebDAV server until the file is unlocked. When the document is closed, the file content is being automatically sent to the server. Finally, the document is automatically unlocked

-

Any temporary Microsoft Office and AutoCAD documents (~$docfile.docx, G57BURP.tmp, etc) are stored in the local file system only and are NOT synchronized to the server. Typically temporary Microsoft Office and AutoCAD documents are being automatically deleted by Microsoft office when the document editing is completed.

-

Locks and eTags are being stored in the application data folder, under the path that corresponds with the mounted drive. For example: C:\Users\<user>\AppData\Local\WebDAV Drive\C\Users\<user>\DAV\ServerData\

-

File ETags

-

When listing folder content the client reads file eTags and saves them in the local file system. When sending modified content to the server, the client attaches the saved eTag to the request. The server compares eTags to ensure the file was not modified on the server since the file was read by the client. In the case of Microsoft Office documents, eTags provide one more level of protection against overwriting server changes in addition to locks.

-

In-Sync Status and ETag Usage For Synchronization

-

Synchronization in this sample is based on In-Sync file status as well as on the eTag received from the server.

-

The server to client synchronization is performed only if the file on the client is marked as In-Sync with the server (it is marked withIn-Sync icon or Pinned file or Cloud fileicon).

-

When any file or folder on the client is updated, it is marked as not In-SyncNot in sync icon, which means the content must be sent to the server. When the updated file is being sent from client to server, the server compares the eTag sent from the client with the ETag stored on the server, to avoid the server changes being overwritten. The file is updated only if the eTags match. Otherwise, the file/folder is considered to be in conflict and marked with the conflict icon.

-

Commands

-

For debugging and development purposes this sample provides the following console commands:

-
    -
  • 'Esc' - simulates app uninstall. The sync root will be unregistered and all files will be deleted.
  • -
  • 'Space' - simulates machine reboot or application failure. The application exits without unregistering the sync root. All files and folders placeholders, their attributes, and states remain in the user file system. 
  • -
  • 'e' - starts/stops the Engine and all sync services. When the Engine is stopped, the user can still edit hydrated documents but can not hydrate files, access offline folders, or sync with remote storage.
  • -
  • 's' - starts/stops full synchronization service. The full synchronization service periodically syncs remote storage and user file system based on each file eTag and in-sync status.   
  • -
  • 'm' - starts/stops remote storage monitor. When the remote storage monitor is stopped the sample does not receive notifications from the remote storage.
  • -
  • 'l' - opens a log file.
  • -
  • 'b' - opens Help & Support portal to submit support tickets, report bugs, suggest features.
  • -
-

See Also:

- - +˙ž \ No newline at end of file diff --git a/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj b/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj index 70d574a..abdd26b 100644 --- a/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj +++ b/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj @@ -12,7 +12,7 @@ IT HIT LTD. - + diff --git a/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj b/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj index f346169..560226a 100644 --- a/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj +++ b/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj @@ -1,5 +1,4 @@ -ļ»æ - + net5.0-windows10.0.19041.0 true @@ -10,25 +9,21 @@ WebDAV Drive AnyCPU;x64 - - - + - - True @@ -36,7 +31,6 @@ Resources.resx - Designer @@ -52,11 +46,9 @@ PublicResXFileCodeGenerator - $(DefaultXamlRuntime) - - + \ No newline at end of file diff --git a/Windows/WebDAVDrive/WebDAVDrive.UI/WindowsTrayInterface.cs b/Windows/WebDAVDrive/WebDAVDrive.UI/WindowsTrayInterface.cs index 958e593..0b073ed 100644 --- a/Windows/WebDAVDrive/WebDAVDrive.UI/WindowsTrayInterface.cs +++ b/Windows/WebDAVDrive/WebDAVDrive.UI/WindowsTrayInterface.cs @@ -9,6 +9,8 @@ using ITHit.FileSystem.Samples.Common; using System.Reflection; +using ITHit.FileSystem.Windows; + namespace WebDAVDrive.UI { /// diff --git a/Windows/WebDAVDrive/WebDAVDrive/Program.cs b/Windows/WebDAVDrive/WebDAVDrive/Program.cs index adc394f..85eeaea 100644 --- a/Windows/WebDAVDrive/WebDAVDrive/Program.cs +++ b/Windows/WebDAVDrive/WebDAVDrive/Program.cs @@ -111,9 +111,9 @@ static async Task Main(string[] args) Settings.WebDAVServerUrl, Settings.WebSocketServerUrl, Settings.IconsFolderPath, - Settings.SyncIntervalMs, logFormatter)) { + Engine.OutgoingSyncService.SyncIntervalMs = Settings.SyncIntervalMs; Engine.AutoLock = Settings.AutoLock; // Start tray application in a separate thread. @@ -317,18 +317,18 @@ private static async Task ProcessUserInputAsync() case ConsoleKey.S: // Start/stop full synchronization. - if (Engine.SyncService.SyncState == SynchronizationState.Disabled) + if (Engine.OutgoingSyncService.SyncState == SynchronizationState.Disabled) { if (Engine.State != EngineState.Running) { - Engine.SyncService.Logger.LogError("Failed to start. The Engine must be running."); + Engine.OutgoingSyncService.Logger.LogError("Failed to start. The Engine must be running."); break; } - await Engine.SyncService.StartAsync(); + await Engine.OutgoingSyncService.StartAsync(); } else { - await Engine.SyncService.StopAsync(); + await Engine.OutgoingSyncService.StopAsync(); } break; diff --git a/Windows/WebDAVDrive/WebDAVDrive/RemoteStorageMonitor.cs b/Windows/WebDAVDrive/WebDAVDrive/RemoteStorageMonitor.cs index a5b74ff..fb7a89d 100644 --- a/Windows/WebDAVDrive/WebDAVDrive/RemoteStorageMonitor.cs +++ b/Windows/WebDAVDrive/WebDAVDrive/RemoteStorageMonitor.cs @@ -1,9 +1,3 @@ -using ITHit.FileSystem; -using ITHit.FileSystem.Samples.Common; -using ITHit.FileSystem.Samples.Common.Windows; -using ITHit.FileSystem.Windows; -using ITHit.WebDAV.Client; -using log4net; using System; using System.IO; using System.Linq; @@ -14,6 +8,13 @@ using System.Threading; using System.Threading.Tasks; +using ITHit.FileSystem; +using ITHit.FileSystem.Samples.Common; +using ITHit.FileSystem.Samples.Common.Windows; +using ITHit.FileSystem.Windows; +using ITHit.WebDAV.Client; + + namespace WebDAVDrive { /// @@ -277,7 +278,7 @@ private async Task ChangedAsync(string remoteStoragePath) catch (IOException ex) { // The file is blocked in the user file system. This is a normal behaviour. - Logger.LogMessage(ex.Message); + Logger.LogDebug(ex.Message); } catch (Exception ex) { diff --git a/Windows/WebDAVDrive/WebDAVDrive/SparsePackage/appxmanifest.xml b/Windows/WebDAVDrive/WebDAVDrive/SparsePackage/appxmanifest.xml index 61f952d..a612190 100644 --- a/Windows/WebDAVDrive/WebDAVDrive/SparsePackage/appxmanifest.xml +++ b/Windows/WebDAVDrive/WebDAVDrive/SparsePackage/appxmanifest.xml @@ -11,6 +11,7 @@ xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" IgnorableNamespaces="uap rescap uap10"> + GetThumbnailAsync(uint size) return thumbnail; } + private static async Task StreamToByteArrayAsync(Stream stream) + { + using (MemoryStream memoryStream = new()) + { + await stream.CopyToAsync(memoryStream); + return memoryStream.ToArray(); + } + } + + /// public async Task> GetPropertiesAsync() { @@ -236,16 +247,6 @@ public async Task> GetPropertiesAsync() return props; } - private static async Task StreamToByteArrayAsync(Stream stream) - { - using (MemoryStream memoryStream = new()) - { - await stream.CopyToAsync(memoryStream); - return memoryStream.ToArray(); - } - } - - /// public Task GetMetadataAsync() { diff --git a/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj b/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj index 94e644f..b8c575b 100644 --- a/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj +++ b/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj @@ -32,7 +32,7 @@ - + diff --git a/macOS/README.md b/macOS/README.md index 1563abc..46b134b 100644 --- a/macOS/README.md +++ b/macOS/README.md @@ -1,74 +1 @@ - -

Virtual File System Sample for Mac in .NET, C#

-

This sample implements a virtual file system for Mac with synchronization support and folders on-demand listing. It synchronizes files and folders both from remote storage to the user file system and from the user file system to remote storage. This sample is written in Xamarin, .NET/C#. To simulate the remote storage, this sample is using a folder in the local file system on the same machine. 

-

The purpose of this sample is to demonstrate the major features of the IT Hit User File System for .NET on macOS and provide patterns for its programming. You will use this sample as a starting point for creating a OneDrive-like file system for macOS for your DMS/CRM/ERP and will reprogram it to publish data from your real storage instead of the local file system.

-

You can download this sample and a trial license in the product download area. You can also clone it and browse the code on GitHub

-

Requirements

-
    -
  • macOS 11.0+
  • -
  • Mono 6.12.0.122+
  • -
  • Xamarin.Mac: 7.4.0.10+
  • -
  • Xcode 12.4+
  • -
  • Visual Studio Community 2019 for Mac v8.8.10+, Stable Channel.
  • -
-

Solution Structure

-

The macOS sample solution consists of 3 projects: container application, an extension project, and a common code.

-

The container application provides a Menu Bar icon to install/uninstall the file system extension. Inside the container application, you should change the hardcoded directory to replicate in your Virtual Disk manually. Consider that the extension can only access sandbox folders (including Downloads, Pictures, Music, Movies). It means that it won't be able to show the contents of the folder outside of the sandbox.

-

The extension project runs in the background and implements a virtual file system on macOS (File Provider). It processes requests from macOS applications sent via macOS file system API and lists folders content. The macOS extension can be installed only as part of a container application, you can not install the extension application by itself.

-

Configuring the Sample

-

In the following steps, we will describe how to configure and run this sample in the development environment. You will create an Apple group ID, Apple app identifies, and Apple provisioning profiles. Then you will update the sample container application project and extension project to use the created IDs and profiles.

-

Log-in to the Apple developer account here: https://developer.apple.com/. To complete the steps below you must have an App Manager role.

-
    -
  1. -

    Create App Group. Navigate to Certificates, IDs, Profiles -> Identifiers -> App Groups and create a new group.

    -
  2. -
  3. -

    Create Apple macOS App IDs. Navigate to Certificates, IDs, Profile -> Identifiers -> App IDs. Create 2 identifiers that will be unique for your project. One will be used for container application another – for the extension.

    -
  4. -
  5. -

    Add app identifiers to the group. Add both identifiers created in Step 2 to the group created in Step 1. Select identifier and click on Edit. Then check the App Groups checkbox, select the Edit button and select the group created in Step 1.

    -
  6. -
  7. -

    Create Provisioning Profiles. Navigate to Certificates, Identifiers & Profiles -> Profiles -> Development and create profile. Associate profile with extension ID and container ID respectively.

    -
  8. -
  9. -

    Download profiles and certificates in XCode. Run XCode and go to Xcode Menu > Preferences -> Accounts tab. Select team and click on “Download Manual Profiles”. You can find more detailed instructions: here

    -
  10. -
  11. -

    Set bundle identifier name in Container project. The bundle identifier is located in VirtualFilesystemMacApp/Info.plist file. You can edit it either in Visual Studio or directly in Info.plist file in the CFBundleIdentifier field (by default it is set to com.userfilesystem.vfs.app). You must set this identifier to the value specified in Step 2.

    -
  12. -
  13. -

    Set bundle identifier name in the Extension project. The bundle identifier is located in FileProviderExtension/Info.plist file. You can edit it either in Visual Studio or directly in Info.plist file in the CFBundleIdentifier field (by default it is set to com.userfilesystem.vfs.app.extension). You must set this identifier to the value specified in Step 2.

    -
  14. -
  15. -

    Configure macOS bundle signing in Container and Extension projects. For each project in Visual Studio go to the project Options. Select Mac Signing and check 'Sign the application bundle'. Select Identity and Provisioning profile.

    -
  16. -
  17. -

    Configure application permissions in Container and Extension projects. Select Entitlements.plist and select group created in Step 1 in App Groups field for each project.

    -
  18. -
  19. -

    Set App Group ID in code. Edit AppGroupsSettings.cs file located in /VirtualFilesystemCommon/ folder. Specify AppGroupId.

    -
  20. -
  21. -

    Set the license. Download the license file here. With the trial license, the product is fully functional and does not have any limitations. As soon as the trial license expires the product will stop working. Open the VirtualFilesystemMacApp/Resources/appsettings.json file and set the License string: 

    -
    "License": "<?xml version='1.0'...",
    -
  22. -
  23. -

    Set the remote storage directory. Here you must set the path to the folder that simulates your remote storage. Your virtual drive will mirror files and folders from this folder. Note that the extension runs as a sandboxed application and has access to a limited number of locations in the local file system. To simulate the remote storage structure you can copy the \RemoteStorage\ folder provided with the project under the ~/Pictures/ folder. To set the remote storage directory open the VirtualFilesystemMacApp/Resources/appsettings.json file and set RemoteStorageRootPath string. Make sure to replace the ${USER} with a real user name:

    -
    "RemoteStorageRootPath": "/Users/User1/Pictures/RemoteStorage"
    -
  24. -
-

Now you are ready to compile and run the project.

-

Running the Sample

-

To run the sample open the project in Visual Studio and run the project. The application adds an application to the macOS Status Bar. To mount the file system select the 'Install Extension' command in the Status Bar.

-

Virtual File System Mac in .NET/C#

-

Note, that every File Provider Extension runs in a sandbox, so access to the local filesystem restricted by OS except Downloads, Pictures, Music, Movies public directories.

-

Troubleshooting

-

If you experience issues on application start it may be caused by an incorrect app configuration. You can find what may be wrong using a macOS Console:

-

Virtual Drive .NET macOS Console

-

If your application started successfully but you experience issues with the file system you may need to filter logs to find information sent by the file system provider by using the 'ITHit' search:

-

Virtual File System macOS Console

-

You can select and copy the console output and submit it to the Help & Support system.

-

Next Article:

-Virtual Drive Sample in .NET, C# - +˙ž \ No newline at end of file