Closed aupous closed 1 year ago
Do those capabilities work when you run the corresponding C++ sample from Microsoft?
Hi @dahall , thank you for your reply. Yes they work well on C++ sample from Microsoft.
Ok. I'll check into the code I converted to see where I went wrong.
Since you have the code there, can you try the following changes to ShellService.InitAndStartServiceTask():
public static void InitAndStartServiceTask()
{
var thread = new Thread(() =>
{
CoInitializeEx(default, COINIT.COINIT_APARTMENTTHREADED).ThrowIfFailed();
uint cookie;
var thumbnailProvider = new ThumbnailProvider();
CoRegisterClassObject(typeof(ThumbnailProvider).GUID, thumbnailProvider, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
var contextMenu = new TestExplorerCommandHandler();
CoRegisterClassObject(typeof(TestExplorerCommandHandler).GUID, contextMenu, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
var customStateProvider = new CustomStateProvider();
CoRegisterClassObject(typeof(CustomStateProvider).GUID, customStateProvider, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
var uriSource = new UriSource();
CoRegisterClassObject(typeof(UriSource).GUID, uriSource, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
using var dummyEvent = CreateEvent(null, false, false);
if (dummyEvent.IsInvalid)
Win32Error.ThrowLastError();
CoWaitForMultipleHandles(COWAIT_FLAGS.COWAIT_DISPATCH_CALLS, INFINITE, 1, new[] { (IntPtr)dummyEvent }, out _);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
I have tried your code above but it doesn't work
It doesn't build, or it fails to fix the problems you originally reported?
The program built successfully, I can run it then placeholder files were created. But thumbnail and custom command on right click not shown, just like before
Hi @dahall , did you have anything updated? I try to understand why it didn't work but I have little experience with C# and C++ so I can't
I've been working on it and have made lots of changes, but the sample still fails. I'll keep trying.
To see if it was the base library calls, I enhanced the testing over on Vanara. There are working examples of the Cloud API there that may be of use: https://github.com/dahall/Vanara/tree/master/UnitTests/PInvoke/CldApi
CoRegisterClassObject function (combaseapi.h) - Win32 apps | Microsoft Learn
At startup, a multiple-use EXE object application must create a class object (with the IClassFactory interface on it), and call CoRegisterClassObject to register the class object.
CoRegisterClassObject will register a kind of factory rather than class object itself like ThumbnailProvider
.
This may help on regstration of ShellServices.
diff --git a/CloudMirror/CloudProviderRegistrar.cs b/CloudMirror/CloudProviderRegistrar.cs
index a221439..54990d7 100644
--- a/CloudMirror/CloudProviderRegistrar.cs
+++ b/CloudMirror/CloudProviderRegistrar.cs
@@ -20,7 +20,7 @@ namespace CloudMirror
{
var info = new StorageProviderSyncRootInfo();
info.Id = GetSyncRootId();
- var folder = StorageFolder.GetFolderFromPathAsync(ProviderFolderLocations.ClientFolder).GetResults();
+ var folder = StorageFolder.GetFolderFromPathAsync(ProviderFolderLocations.ClientFolder).AsTask().Result;
info.Path = folder;
info.DisplayNameResource = "TestStorageProviderDisplayName";
info.IconResource = "%SystemRoot%\\system32\\charmap.exe,0"; // This icon is just for the sample. You should provide your own branded icon here
diff --git a/CloudMirror/ShellServices.cs b/CloudMirror/ShellServices.cs
index f9a0eea..b1a77f1 100644
--- a/CloudMirror/ShellServices.cs
+++ b/CloudMirror/ShellServices.cs
@@ -1,6 +1,5 @@
using System;
using System.Threading;
-using System.Threading.Tasks;
using Vanara.PInvoke;
using static Vanara.PInvoke.Kernel32;
using static Vanara.PInvoke.Ole32;
@@ -15,17 +14,17 @@ namespace CloudMirror
{
CoInitializeEx(default, COINIT.COINIT_APARTMENTTHREADED).ThrowIfFailed();
- var thumbnailProvider = new ThumbnailProvider();
- CoRegisterClassObject(typeof(ThumbnailProvider).GUID, thumbnailProvider, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out var cookie).ThrowIfFailed();
+ var thumbnailProviderFactory = new Factory(() => new ThumbnailProvider());
+ CoRegisterClassObject(typeof(ThumbnailProvider).GUID, thumbnailProviderFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out var cookie).ThrowIfFailed();
- var contextMenu = new TestExplorerCommandHandler();
- CoRegisterClassObject(typeof(TestExplorerCommandHandler).GUID, contextMenu, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
+ var contextMenuFactory = new Factory(() => new TestExplorerCommandHandler());
+ CoRegisterClassObject(typeof(TestExplorerCommandHandler).GUID, contextMenuFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
- var customStateProvider = new CustomStateProvider();
- CoRegisterClassObject(typeof(CustomStateProvider).GUID, customStateProvider, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
+ var customStateProviderFactory = new Factory(() => new CustomStateProvider());
+ CoRegisterClassObject(typeof(CustomStateProvider).GUID, customStateProviderFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
- var uriSource = new UriSource();
- CoRegisterClassObject(typeof(UriSource).GUID, uriSource, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
+ var uriSourceFactory = new Factory(() => new UriSource());
+ CoRegisterClassObject(typeof(UriSource).GUID, uriSourceFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
using var dummyEvent = CreateEvent(null, false, false);
if (dummyEvent.IsInvalid)
@@ -35,5 +34,37 @@ namespace CloudMirror
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
+
+ private class Factory : IClassFactory
+ {
+ public Factory(Func<object> generator)
+ {
+ _generator = generator;
+ }
+
+ private readonly Func<object> _generator;
+ private static readonly Guid IID_IUnknown = new Guid("{00000000-0000-0000-c000-000000000046}");
+
+ HRESULT IClassFactory.CreateInstance(object pUnkOuter, in Guid riid, out object ppvObject)
+ {
+ if (riid != IID_IUnknown)
+ {
+ // We cannot handle this for now
+ ppvObject = null;
+ return HRESULT.E_NOINTERFACE;
+ }
+ else
+ {
+ var obj = _generator();
+ ppvObject = obj;
+ return HRESULT.S_OK;
+ }
+ }
+
+ HRESULT IClassFactory.LockServer(bool fLock)
+ {
+ return HRESULT.S_OK;
+ }
+ }
}
}
With this fix, it worked on my Windows 10 21H2.
diff --git a/CloudMirror/CloudProviderRegistrar.cs b/CloudMirror/CloudProviderRegistrar.cs
index a221439..54990d7 100644
--- a/CloudMirror/CloudProviderRegistrar.cs
+++ b/CloudMirror/CloudProviderRegistrar.cs
@@ -20,7 +20,7 @@ namespace CloudMirror
{
var info = new StorageProviderSyncRootInfo();
info.Id = GetSyncRootId();
- var folder = StorageFolder.GetFolderFromPathAsync(ProviderFolderLocations.ClientFolder).GetResults();
+ var folder = StorageFolder.GetFolderFromPathAsync(ProviderFolderLocations.ClientFolder).AsTask().Result;
info.Path = folder;
info.DisplayNameResource = "TestStorageProviderDisplayName";
info.IconResource = "%SystemRoot%\\system32\\charmap.exe,0"; // This icon is just for the sample. You should provide your own branded icon here
diff --git a/CloudMirror/FakeCloudProvider.cs b/CloudMirror/FakeCloudProvider.cs
index f149199..cf94bab 100644
--- a/CloudMirror/FakeCloudProvider.cs
+++ b/CloudMirror/FakeCloudProvider.cs
@@ -1,5 +1,5 @@
using System;
-using System.Threading.Tasks;
+using System.Threading;
using static Vanara.PInvoke.CldApi;
namespace CloudMirror
@@ -20,6 +20,8 @@ namespace CloudMirror
if (ProviderFolderLocations.Init(serverFolder, clientFolder))
{
+ var onStop = new CancellationTokenSource();
+
try
{
// Stage 1: Setup
@@ -27,7 +29,7 @@ namespace CloudMirror
// The client folder (syncroot) must be indexed in order for states to properly display
Utilities.AddFolderToSearchIndexer(ProviderFolderLocations.ClientFolder);
// Start up the task that registers and hosts the services for the shell (such as custom states, menus, etc)
- ShellServices.InitAndStartServiceTask();
+ ShellServices.InitAndStartServiceTask(onStop.Token);
// Register the provider with the shell so that the Sync Root shows up in File Explorer
CloudProviderRegistrar.RegisterWithShell();
// Hook up callback methods (in this class) for transferring files between client and server
@@ -50,6 +52,8 @@ namespace CloudMirror
// Stage 3: Done Running-- caused by CTRL-C
//--------------------------------------------------------------------------------------------
// Unhook up those callback methods
+ onStop.Cancel();
+
DisconnectSyncRootTransferCallbacks();
// A real sync engine should NOT unregister the sync root upon exit.
diff --git a/CloudMirror/FileCopierWithProgress.cs b/CloudMirror/FileCopierWithProgress.cs
index 2e13c69..bc3d2d9 100644
--- a/CloudMirror/FileCopierWithProgress.cs
+++ b/CloudMirror/FileCopierWithProgress.cs
@@ -194,6 +194,22 @@ namespace CloudMirror
readContext.StartOffset += numberOfBytesTransfered;
readContext.RemainingLength -= numberOfBytesTransfered;
+ Marshal.WriteInt64(
+ ptr: IntPtr.Add(
+ pointer: (IntPtr)overlapped,
+ offset: (int)Marshal.OffsetOf<READ_COMPLETION_CONTEXT>(nameof(readContext.StartOffset))
+ ),
+ val: readContext.StartOffset
+ );
+
+ Marshal.WriteInt64(
+ ptr: IntPtr.Add(
+ pointer: (IntPtr)overlapped,
+ offset: (int)Marshal.OffsetOf<READ_COMPLETION_CONTEXT>(nameof(readContext.RemainingLength))
+ ),
+ val: readContext.RemainingLength
+ );
+
// See if there is anything left to read
if (readContext.RemainingLength > 0)
{
@@ -244,7 +260,7 @@ namespace CloudMirror
};
var opParams = new CF_OPERATION_PARAMETERS
{
- ParamSize = (uint)Marshal.SizeOf<CF_OPERATION_PARAMETERS.TRANSFERDATA>() + (uint)Marshal.SizeOf<uint>(),
+ ParamSize = (uint)CF_OPERATION_PARAMETERS.CF_SIZE_OF_OP_PARAM<CF_OPERATION_PARAMETERS.TRANSFERDATA>(),
TransferData = new CF_OPERATION_PARAMETERS.TRANSFERDATA
{
CompletionStatus = completionStatus,
diff --git a/CloudMirror/ShellServices.cs b/CloudMirror/ShellServices.cs
index f9a0eea..68a5f5d 100644
--- a/CloudMirror/ShellServices.cs
+++ b/CloudMirror/ShellServices.cs
@@ -1,6 +1,5 @@
using System;
using System.Threading;
-using System.Threading.Tasks;
using Vanara.PInvoke;
using static Vanara.PInvoke.Kernel32;
using static Vanara.PInvoke.Ole32;
@@ -9,31 +8,68 @@ namespace CloudMirror
{
internal static class ShellServices
{
- public static void InitAndStartServiceTask()
+ public static void InitAndStartServiceTask(CancellationToken cancellationToken)
{
var thread = new Thread(() =>
{
CoInitializeEx(default, COINIT.COINIT_APARTMENTTHREADED).ThrowIfFailed();
- var thumbnailProvider = new ThumbnailProvider();
- CoRegisterClassObject(typeof(ThumbnailProvider).GUID, thumbnailProvider, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out var cookie).ThrowIfFailed();
+ var thumbnailProviderFactory = new Factory(() => new ThumbnailProvider());
+ CoRegisterClassObject(typeof(ThumbnailProvider).GUID, thumbnailProviderFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out var thumbnailProviderFactoryCookie).ThrowIfFailed();
- var contextMenu = new TestExplorerCommandHandler();
- CoRegisterClassObject(typeof(TestExplorerCommandHandler).GUID, contextMenu, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
+ var contextMenuFactory = new Factory(() => new TestExplorerCommandHandler());
+ CoRegisterClassObject(typeof(TestExplorerCommandHandler).GUID, contextMenuFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out var contextMenuFactoryCookie).ThrowIfFailed();
- var customStateProvider = new CustomStateProvider();
- CoRegisterClassObject(typeof(CustomStateProvider).GUID, customStateProvider, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
+ var customStateProviderFactory = new Factory(() => new CustomStateProvider());
+ CoRegisterClassObject(typeof(CustomStateProvider).GUID, customStateProviderFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out var customStateProviderFactoryCookie).ThrowIfFailed();
- var uriSource = new UriSource();
- CoRegisterClassObject(typeof(UriSource).GUID, uriSource, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out cookie).ThrowIfFailed();
+ var uriSourceFactory = new Factory(() => new UriSource());
+ CoRegisterClassObject(typeof(UriSource).GUID, uriSourceFactory, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out var uriSourceFactoryCookie).ThrowIfFailed();
- using var dummyEvent = CreateEvent(null, false, false);
- if (dummyEvent.IsInvalid)
- Win32Error.ThrowLastError();
- CoWaitForMultipleHandles(COWAIT_FLAGS.COWAIT_DISPATCH_CALLS, INFINITE, 1, new[] { (IntPtr)dummyEvent }, out _);
+ var stopEvent = cancellationToken.WaitHandle.SafeWaitHandle.DangerousGetHandle();
+ CoWaitForMultipleHandles(COWAIT_FLAGS.COWAIT_DISPATCH_CALLS, INFINITE, 1, new[] { (IntPtr)stopEvent }, out _);
+
+ CoRevokeClassObject(uriSourceFactoryCookie);
+ CoRevokeClassObject(customStateProviderFactoryCookie);
+ CoRevokeClassObject(contextMenuFactoryCookie);
+ CoRevokeClassObject(thumbnailProviderFactoryCookie);
+
+ CoUninitialize();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
+
+ private class Factory : IClassFactory
+ {
+ public Factory(Func<object> generator)
+ {
+ _generator = generator;
+ }
+
+ private readonly Func<object> _generator;
+ private static readonly Guid IID_IUnknown = new Guid("{00000000-0000-0000-c000-000000000046}");
+
+ HRESULT IClassFactory.CreateInstance(object pUnkOuter, in Guid riid, out object ppvObject)
+ {
+ if (riid != IID_IUnknown)
+ {
+ // We cannot handle this for now
+ ppvObject = null;
+ return HRESULT.E_NOINTERFACE;
+ }
+ else
+ {
+ var obj = _generator();
+ ppvObject = obj;
+ return HRESULT.S_OK;
+ }
+ }
+
+ HRESULT IClassFactory.LockServer(bool fLock)
+ {
+ return HRESULT.S_OK;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/CloudMirror/Utilities.cs b/CloudMirror/Utilities.cs
index 0ea27fc..234ae4c 100644
--- a/CloudMirror/Utilities.cs
+++ b/CloudMirror/Utilities.cs
@@ -81,7 +81,7 @@ namespace CloudMirror
propStoreVolatile.Item.SetValue(PKEY_StorageProviderTransferProgress, new long[] { completed, total }, false);
// Set the sync transfer status accordingly
- propStoreVolatile.Item.SetValue(PROPERTYKEY.System.SyncTransferStatus, (completed < total) ? SYNC_TRANSFER_STATUS.STS_TRANSFERRING : SYNC_TRANSFER_STATUS.STS_NONE, false);
+ propStoreVolatile.Item.SetValue(PROPERTYKEY.System.SyncTransferStatus, (uint)((completed < total) ? SYNC_TRANSFER_STATUS.STS_TRANSFERRING : SYNC_TRANSFER_STATUS.STS_NONE), false);
// Without this, all your hard work is wasted.
propStoreVolatile.Item.Commit();
@kenjiuno Will you submit a PR with those changes?
I will
Thank you!!
I have ran example CloudMirror, I found that the program can create Placeholder file, but some ShellServices didn't work. I cannot see file thumbnail, and TestCommand not showed neither. What is the problem?