Skip to content

Commit d8b65ed

Browse files
committed
v3.3.9488.0
1 parent cbd5a1c commit d8b65ed

File tree

20 files changed

+527
-73
lines changed

20 files changed

+527
-73
lines changed

Common/Common.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
<Description>Contains functionality common for all Virtual Drive samples, both for Windows and macOS.</Description>
99
</PropertyGroup>
1010
<ItemGroup>
11-
<PackageReference Include="ITHit.FileSystem" Version="3.2.8936.0" />
11+
<PackageReference Include="ITHit.FileSystem" Version="3.3.9488.0" />
1212
</ItemGroup>
1313
</Project>

Windows/Common/Common.Windows.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.1" />
1313
</ItemGroup>
1414
<ItemGroup>
15-
<PackageReference Include="ITHit.FileSystem.Windows" Version="3.2.8936.0" />
15+
<PackageReference Include="ITHit.FileSystem.Windows" Version="3.3.9488.0" />
1616
<ProjectReference Include="..\..\Common\Common.csproj" />
1717
</ItemGroup>
1818
</Project>

Windows/VirtualDrive/VirtualDrive.Package/Package.appxmanifest

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<Identity
1212
Name="38f8ee88-19d0-48a3-ad8e-904a6cf3ca17"
13-
Publisher="CN=VirtualFileSystemPackagePublisher"
13+
Publisher="CN=IT Hit LTD., O=IT Hit LTD., STREET=86 Kimbark blvd, L=Toronto, S=Ontario, PostalCode=M5N2Y1, C=CA"
1414
Version="1.0.0.0" />
1515

1616
<Properties>
@@ -45,7 +45,7 @@
4545
<desktop3:Extension Category="windows.cloudFiles">
4646
<desktop3:CloudFiles>
4747
<desktop3:ThumbnailProviderHandler Clsid="05CF065E-E135-4B2B-9D4D-CFB3FBAC73A4"/>
48-
<!--<desktop3:ThumbnailProviderHandler Clsid="b9e1bc83-84ed-4ff6-984b-818f9564a7eb"/>-->
48+
4949
<desktop3:CustomStateHandler Clsid="20000000-0000-0000-0000-000000000001"/>
5050
<desktop3:ExtendedPropertyHandler Clsid="20000000-0000-0000-0000-000000000001"/>
5151
<desktop3:BannersHandler Clsid="20000000-0000-0000-0000-000000000001"/>
@@ -55,10 +55,11 @@
5555
<com:Extension Category="windows.comServer">
5656
<com:ComServer>
5757
<!-- When changing ProgId make sure to change it also in the ThumbnailProvider.cs -->
58-
<com:SurrogateServer AppId="3000bacb-ea7c-4e37-a939-3b16154da364" DisplayName="VirtualDrive.ThumbnailProvider">
59-
<com:Class Id="05CF065E-E135-4B2B-9D4D-CFB3FBAC73A4" Path="VirtualDrive.ThumbnailProvider\VirtualDrive.ThumbnailProvider.comhost.dll" ThreadingModel="Both" ProgId="VirtualDrive.ThumbnailProvider" />
60-
</com:SurrogateServer>
61-
<com:ProgId Id="VirtualDrive.ThumbnailProvider" Clsid="05CF065E-E135-4B2B-9D4D-CFB3FBAC73A4" />
58+
59+
<com:ExeServer DisplayName="VirtualDrive.ThumbnailProvider" Executable="VirtualDrive.ThumbnailProvider\VirtualDrive.ThumbnailProvider.exe">
60+
<com:Class Id="05CF065E-E135-4B2B-9D4D-CFB3FBAC73A4" />
61+
</com:ExeServer>
62+
6263
</com:ComServer>
6364
</com:Extension>
6465
</Extensions>

Windows/VirtualDrive/VirtualDrive.Package/VirtualDrive.Package.wapproj

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -48,36 +48,6 @@
4848
<DefaultLanguage>en-US</DefaultLanguage>
4949
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
5050
<EntryPointProjectUniqueName>..\VirtualDrive\VirtualDrive.csproj</EntryPointProjectUniqueName>
51-
<GenerateAppInstallerFile>True</GenerateAppInstallerFile>
52-
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
53-
<GenerateTestArtifacts>True</GenerateTestArtifacts>
54-
<AppxBundlePlatforms>x64</AppxBundlePlatforms>
55-
<AppInstallerUri></AppInstallerUri>
56-
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
57-
</PropertyGroup>
58-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
59-
<AppxBundle>Always</AppxBundle>
60-
</PropertyGroup>
61-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
62-
<AppxBundle>Always</AppxBundle>
63-
</PropertyGroup>
64-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
65-
<AppxBundle>Always</AppxBundle>
66-
</PropertyGroup>
67-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
68-
<AppxBundle>Always</AppxBundle>
69-
</PropertyGroup>
70-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
71-
<AppxBundle>Always</AppxBundle>
72-
</PropertyGroup>
73-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
74-
<AppxBundle>Always</AppxBundle>
75-
</PropertyGroup>
76-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
77-
<AppxBundle>Always</AppxBundle>
78-
</PropertyGroup>
79-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
80-
<AppxBundle>Always</AppxBundle>
8151
</PropertyGroup>
8252
<ItemGroup>
8353
<AppxManifest Include="Package.appxmanifest">
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
using System.Diagnostics;
4+
5+
namespace VirtualDrive.ThumbnailProvider.ComInfrastructure
6+
{
7+
[ComImport]
8+
[ComVisible(false)]
9+
[Guid("00000001-0000-0000-C000-000000000046")]
10+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
11+
internal interface IClassFactory
12+
{
13+
void CreateInstance(
14+
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
15+
ref Guid riid,
16+
out IntPtr ppvObject);
17+
18+
void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
19+
}
20+
21+
/// <summary>
22+
/// Implementation of ClassFactory to create com objects.
23+
/// This is standard mechanism in case out-of-proc com servers.
24+
/// </summary>
25+
[ComVisible(true)]
26+
internal class BasicClassFactory<T> : IClassFactory where T : new()
27+
{
28+
public void CreateInstance(
29+
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
30+
ref Guid riid,
31+
out IntPtr ppvObject)
32+
{
33+
Type interfaceType = GetValidatedInterfaceType(typeof(T), ref riid, pUnkOuter);
34+
35+
object obj = new T();
36+
if (pUnkOuter != null)
37+
{
38+
obj = CreateAggregatedObject(pUnkOuter, obj);
39+
}
40+
41+
ppvObject = GetObjectAsInterface(obj, interfaceType);
42+
}
43+
44+
public void LockServer([MarshalAs(UnmanagedType.Bool)] bool serverLock)
45+
{
46+
if (serverLock)
47+
ReferenceManager.LockServer();
48+
else
49+
ReferenceManager.UnlockServer();
50+
}
51+
52+
private static readonly Guid IID_IUnknown = Guid.Parse("00000000-0000-0000-C000-000000000046");
53+
54+
private static Type GetValidatedInterfaceType(Type classType, ref Guid riid, object outer)
55+
{
56+
if (riid == IID_IUnknown)
57+
{
58+
return typeof(object);
59+
}
60+
61+
// Aggregation can only be done when requesting IUnknown.
62+
if (outer != null)
63+
{
64+
throw new COMException(string.Empty, unchecked((int)0x80040110));
65+
}
66+
67+
// Verify the class implements the desired interface
68+
foreach (Type i in classType.GetInterfaces())
69+
{
70+
if (i.GUID == riid)
71+
{
72+
return i;
73+
}
74+
}
75+
76+
// E_NOINTERFACE
77+
throw new InvalidCastException();
78+
}
79+
80+
private static IntPtr GetObjectAsInterface(object obj, Type interfaceType)
81+
{
82+
// If the requested "interface type" is type object then return as IUnknown
83+
if (interfaceType == typeof(object))
84+
{
85+
return Marshal.GetIUnknownForObject(obj);
86+
}
87+
88+
IntPtr interfaceMaybe = Marshal.GetComInterfaceForObject(obj, interfaceType, CustomQueryInterfaceMode.Ignore);
89+
if (interfaceMaybe == IntPtr.Zero)
90+
{
91+
// E_NOINTERFACE
92+
throw new InvalidCastException();
93+
}
94+
95+
return interfaceMaybe;
96+
}
97+
98+
private static object CreateAggregatedObject(object pUnkOuter, object comObject)
99+
{
100+
IntPtr outerPtr = Marshal.GetIUnknownForObject(pUnkOuter);
101+
102+
try
103+
{
104+
IntPtr innerPtr = Marshal.CreateAggregatedObject(outerPtr, comObject);
105+
return Marshal.GetObjectForIUnknown(innerPtr);
106+
}
107+
finally
108+
{
109+
// Decrement the above 'Marshal.GetIUnknownForObject()'
110+
Marshal.Release(outerPtr);
111+
}
112+
}
113+
}
114+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Threading;
3+
4+
namespace VirtualDrive.ThumbnailProvider.ComInfrastructure
5+
{
6+
/// <summary>
7+
/// GcReferencesCleaner forces to cleanup references.
8+
/// It allows to have actual state of Com objects.
9+
/// </summary>
10+
internal class GcReferencesCleaner : IDisposable
11+
{
12+
private const int CheckIntervalMs = 10000;
13+
14+
private readonly Timer timer;
15+
16+
public GcReferencesCleaner()
17+
{
18+
timer = new Timer(o => GC.Collect(), null, CheckIntervalMs, CheckIntervalMs);
19+
}
20+
21+
public void Dispose()
22+
{
23+
timer.Dispose();
24+
}
25+
}
26+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Runtime.InteropServices;
5+
using System.Threading.Tasks;
6+
using VirtualDrive.ThumbnailProvider.Interop;
7+
8+
namespace VirtualDrive.ThumbnailProvider.ComInfrastructure
9+
{
10+
/// <summary>
11+
/// LocalServer encapsulates Com Exe server lifecycle
12+
/// from Com class registration till exit if no object references or server locks.
13+
/// </summary>
14+
public class LocalServer : IDisposable
15+
{
16+
private readonly List<int> registrationCookies = new List<int>();
17+
18+
private int objectRefs = 0;
19+
private int serverLocks = 0;
20+
21+
private GcReferencesCleaner referenceCleaner = new GcReferencesCleaner();
22+
23+
private object exitLock = new object();
24+
25+
private TaskCompletionSource<bool> completionSource;
26+
27+
public LocalServer()
28+
{
29+
ReferenceManager.ObjectCreated += OnObjectCreated;
30+
ReferenceManager.ObjectDestroyed += OnObjectDestroyed;
31+
32+
ReferenceManager.ServerLocked += OnServerLocked;
33+
ReferenceManager.ServerUnlocked += OnServerUnlocked;
34+
}
35+
36+
/// <summary>
37+
/// Com class registration.
38+
/// </summary>
39+
public void RegisterClass<T>(Guid clsid) where T : new()
40+
{
41+
int cookie;
42+
int hr = Ole32.CoRegisterClassObject(ref clsid, new BasicClassFactory<T>(), Ole32.CLSCTX_LOCAL_SERVER, Ole32.REGCLS_MULTIPLEUSE | Ole32.REGCLS_SUSPENDED, out cookie);
43+
if (hr < 0)
44+
{
45+
Marshal.ThrowExceptionForHR(hr);
46+
}
47+
48+
registrationCookies.Add(cookie);
49+
50+
hr = Ole32.CoResumeClassObjects();
51+
if (hr < 0)
52+
{
53+
Marshal.ThrowExceptionForHR(hr);
54+
}
55+
}
56+
57+
/// <summary>
58+
/// Returns task with lidecycle of com server.
59+
/// </summary>
60+
public async Task<bool> Run()
61+
{
62+
completionSource = new TaskCompletionSource<bool>();
63+
64+
return await completionSource.Task;
65+
}
66+
67+
/// <summary>
68+
/// Performs application-defined tasks associated with releasing resources.
69+
/// </summary>
70+
public void Dispose()
71+
{
72+
foreach (int cookie in registrationCookies)
73+
{
74+
int hr = Ole32.CoRevokeClassObject(cookie);
75+
Debug.Assert(hr >= 0, $"CoRevokeClassObject failed ({hr:x}). Cookie: {cookie}");
76+
}
77+
78+
ReferenceManager.ObjectCreated -= OnObjectCreated;
79+
ReferenceManager.ObjectDestroyed -= OnObjectDestroyed;
80+
81+
ReferenceManager.ServerLocked -= OnServerLocked;
82+
ReferenceManager.ServerUnlocked -= OnServerUnlocked;
83+
84+
referenceCleaner.Dispose();
85+
}
86+
87+
/// <summary>
88+
/// Handles com object creation.
89+
/// </summary>
90+
private void OnObjectCreated()
91+
{
92+
lock (exitLock)
93+
{
94+
objectRefs++;
95+
}
96+
}
97+
98+
/// <summary>
99+
/// Handles com object release.
100+
/// </summary>
101+
private void OnObjectDestroyed()
102+
{
103+
lock (exitLock)
104+
{
105+
objectRefs--;
106+
107+
ExitIfNoRefs();
108+
}
109+
}
110+
111+
/// <summary>
112+
/// Handles com server lock.
113+
/// </summary>
114+
private void OnServerLocked()
115+
{
116+
lock (exitLock)
117+
{
118+
serverLocks++;
119+
}
120+
}
121+
122+
/// <summary>
123+
/// Handles com server unlock.
124+
/// </summary>
125+
private void OnServerUnlocked()
126+
{
127+
lock (exitLock)
128+
{
129+
serverLocks--;
130+
131+
ExitIfNoRefs();
132+
}
133+
}
134+
135+
/// <summary>
136+
/// Checks condition to exit and complete lifecycle task.
137+
/// </summary>
138+
private void ExitIfNoRefs()
139+
{
140+
if ((objectRefs <= 0) && (serverLocks <= 0))
141+
{
142+
completionSource.SetResult(true);
143+
}
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)