Skip to content

Commit 97a08d9

Browse files
authored
Merge pull request #80 from Dijji/ProfileOnly
Profile only
2 parents e1a7ec8 + 0ba6cb6 commit 97a08d9

33 files changed

+1087
-179
lines changed

.gitignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,30 @@
3939
/PropertyHandler/x64/Release
4040
/x64/Release
4141
/Setup/obj/x64/Release
42+
/TestDriver/TestDriver.suo
43+
/AssociationManager/bin/Debug
44+
/CommandLine/Debug
45+
/CommandLineAssociationManager/bin/Debug
46+
/ContextMenuHandler/Debug
47+
/Debug
48+
/PropertyHandler/Debug
49+
/Setup/bin
50+
/Setup/obj/x86/Debug
51+
/Setup/msi/Debug
52+
/AssociationManager/bin/Release
53+
/CommandLine/Release
54+
/CommandLineAssociationManager/bin/Release
55+
/ContextMenuHandler/Release
56+
/PropertyHandler/Release
57+
/Release
58+
/Setup/msi/Release
59+
/Setup/obj/x86/Release
60+
/CommandLine/FileMeta.aps
61+
/ContextMenuHandler/ContextMenuHandler.aps
62+
/PropertyHandler/FileMetaPropertyHandler.aps
63+
/TestDriverAssoc/.vs/TestDriverAssoc/v15
64+
/TestSandbox
65+
/TestDriver/bin/Debug
66+
/TestDriverAssoc/bin/Debug
67+
/TestSandbox32
68+
/CommandLineAssociationManager/CommandLineAssociationManager.csproj.user

AssociationManager/Extension.cs

Lines changed: 174 additions & 46 deletions
Large diffs are not rendered by default.

AssociationManager/FileMetaAssociationManager.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
<Compile Include="Extensions.cs" />
112112
<Compile Include="ObservableCollectionWithReset.cs" />
113113
<Compile Include="Profile.cs" />
114+
<Compile Include="PropertyHandlerTest.cs" />
114115
<Compile Include="PropertyListEntry.cs" />
115116
<Compile Include="Code Pack\PropertyKey.cs" />
116117
<Compile Include="Code Pack\PropertySystemNativeMethods.cs" />

AssociationManager/MainView.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ public void AddHandlers()
164164
foreach (Extension ext in SelectedExtensions)
165165
{
166166
ext.SetupHandlerForExtension(SelectedProfile, true);
167+
168+
if (ext.PropertyHandlerState == HandlerState.ProfileOnly)
169+
MessageBox.Show(string.Format(LocalizedMessages.WindowsWontExtendHandler, ext.Name),
170+
LocalizedMessages.SetupHandler);
167171
}
168172
}
169173

@@ -242,7 +246,9 @@ private void DeterminePossibleActions()
242246
break;
243247
}
244248
}
245-
else if (e.PropertyHandlerState == HandlerState.Ours || e.PropertyHandlerState == HandlerState.Chained)
249+
else if (e.PropertyHandlerState == HandlerState.Ours ||
250+
e.PropertyHandlerState == HandlerState.Chained ||
251+
e.PropertyHandlerState == HandlerState.ProfileOnly)
246252
{
247253
if (handlersSelected == null)
248254
handlersSelected = HandlerSet.Ours;

AssociationManager/MainWindow.xaml.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,32 @@ private void profiles_Click(object sender, RoutedEventArgs e)
147147
view.SelectedProfile = state.SelectedProfile;
148148
}
149149

150+
// Temporary code to enumerate extensions for which Add Handler fails
151+
//private void CheckForBlockedExtensions()
152+
//{
153+
// using (var w = new System.IO.StreamWriter(@"D:\TestProperties\Results\Extensions.csv"))
154+
// {
155+
// foreach (var ext in state.Extensions.Where(x => x.PropertyHandlerState == HandlerState.Foreign))
156+
// {
157+
// string result = "Exception";
158+
// try
159+
// {
160+
// result = ext.IsPropertyHandlerBlocked() ? "Blocked" : "Ok";
161+
// }
162+
// catch (System.Exception ex)
163+
// {
164+
// }
165+
// w.WriteLine(String.Format("{0}, {1}, {2}", ext.Name, result, ext.PropertyHandlerDisplay));
166+
// }
167+
// }
168+
//}
169+
150170
private void restartExplorer_Click(object sender, RoutedEventArgs e)
151171
{
172+
// Temporary code to enumerate extensions for which Add Handler fails
173+
//CheckForBlockedExtensions();
174+
//return;
175+
152176
bool failed = false;
153177
try
154178
{
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
using System.Text;
8+
9+
namespace FileMetadataAssociationManager
10+
{
11+
static class PropertyHandlerTest
12+
{
13+
private static Guid IPropertyStoreGuid = new Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99");
14+
15+
public static bool WindowsIgnoresOurPropertyHandler(string extension)
16+
{
17+
// Create a temporary file with the right extension
18+
string fileFullName = Path.ChangeExtension(
19+
Path.GetTempPath() + Guid.NewGuid().ToString(), extension);
20+
using (var fs = File.Create(fileFullName)) { }
21+
22+
// Get the property store that Explorer would use
23+
IPropertyStore ps;
24+
bool result = true;
25+
HResult hr = (HResult)SHGetPropertyStoreFromParsingName(fileFullName, IntPtr.Zero,
26+
GETPROPERTYSTOREFLAGS.GPS_NO_OPLOCK | GETPROPERTYSTOREFLAGS.GPS_HANDLERPROPERTIESONLY, ref IPropertyStoreGuid, out ps);
27+
if (hr == HResult.Ok)
28+
{
29+
// Look for the signature property value that marks the handler as ours
30+
PropertyKey key;
31+
PropVariant pv = new PropVariant();
32+
PropertySystemNativeMethods.PSGetPropertyKeyFromName("System.Software.ProductName", out key);
33+
hr = ps.GetValue(key, pv);
34+
if (hr == HResult.Ok && !pv.IsNullOrEmpty)
35+
{
36+
if ((pv.Value as string) == "FileMetadata")
37+
result = false;
38+
}
39+
Marshal.ReleaseComObject(ps); // This release frees up the file for deletion
40+
}
41+
42+
File.Delete(fileFullName);
43+
44+
return result;
45+
}
46+
47+
[DllImport("shell32.dll", SetLastError = true)]
48+
public static extern int SHGetPropertyStoreFromParsingName(
49+
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
50+
IntPtr zeroWorks,
51+
GETPROPERTYSTOREFLAGS flags,
52+
ref Guid iIdPropStore,
53+
[Out] out IPropertyStore propertyStore);
54+
}
55+
56+
public enum GETPROPERTYSTOREFLAGS
57+
{
58+
// If no flags are specified (GPS_DEFAULT), a read-only property store is returned that includes properties for the file or item.
59+
// In the case that the shell item is a file, the property store contains:
60+
// 1. properties about the file from the file system
61+
// 2. properties from the file itself provided by the file's property handler, unless that file is offline,
62+
// see GPS_OPENSLOWITEM
63+
// 3. if requested by the file's property handler and supported by the file system, properties stored in the
64+
// alternate property store.
65+
//
66+
// Non-file shell items should return a similar read-only store
67+
//
68+
// Specifying other GPS_ flags modifies the store that is returned
69+
GPS_DEFAULT = 0x00000000,
70+
GPS_HANDLERPROPERTIESONLY = 0x00000001, // only include properties directly from the file's property handler
71+
GPS_READWRITE = 0x00000002, // Writable stores will only include handler properties
72+
GPS_TEMPORARY = 0x00000004, // A read/write store that only holds properties for the lifetime of the IShellItem object
73+
GPS_FASTPROPERTIESONLY = 0x00000008, // do not include any properties from the file's property handler (because the file's property handler will hit the disk)
74+
GPS_OPENSLOWITEM = 0x00000010, // include properties from a file's property handler, even if it means retrieving the file from offline storage.
75+
GPS_DELAYCREATION = 0x00000020, // delay the creation of the file's property handler until those properties are read, written, or enumerated
76+
GPS_BESTEFFORT = 0x00000040, // For readonly stores, succeed and return all available properties, even if one or more sources of properties fails. Not valid with GPS_READWRITE.
77+
GPS_NO_OPLOCK = 0x00000080, // some data sources protect the read property store with an oplock, this disables that
78+
GPS_MASK_VALID = 0x000000FF,
79+
}
80+
81+
/// <summary>
82+
/// A property store
83+
/// </summary>
84+
[ComImport]
85+
[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
86+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
87+
interface IPropertyStore
88+
{
89+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
90+
HResult GetCount([Out] out uint propertyCount);
91+
92+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
93+
HResult GetAt([In] uint propertyIndex, out PropertyKey key);
94+
95+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
96+
HResult GetValue([In] ref PropertyKey key, [Out] PropVariant pv);
97+
98+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
99+
HResult SetValue([In] ref PropertyKey key, [In] PropVariant pv);
100+
101+
[PreserveSig]
102+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
103+
HResult Commit();
104+
}
105+
106+
[ComImport]
107+
[Guid("C8E2D566-186E-4D49-BF41-6909EAD56ACC")]
108+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
109+
interface IPropertyStoreCapabilities
110+
{
111+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
112+
HResult IsPropertyWritable([In]ref PropertyKey propertyKey);
113+
}
114+
}

AssociationManager/State.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ public void Populate(string savedStateFile = null)
6868

6969
public void SortExtensions()
7070
{
71-
// Sort by file extension, but group by our handler, chained handlers, other handlers, and finally no handler
71+
// Sort by file extension, but group by our handler, chained handlers, profile only,
72+
// other handlers, and finally no handler
7273
// This uses a Sort extension to ObservableCollection
7374
extensions.Sort((e, f) =>
7475
{
@@ -82,6 +83,10 @@ public void SortExtensions()
8283
return -1;
8384
else if (f.PropertyHandlerState == HandlerState.Chained)
8485
return 1;
86+
else if (e.PropertyHandlerState == HandlerState.ProfileOnly)
87+
return -1;
88+
else if (f.PropertyHandlerState == HandlerState.ProfileOnly)
89+
return 1;
8590
else if (e.PropertyHandlerState == HandlerState.Foreign)
8691
return -1;
8792
else if (f.PropertyHandlerState == HandlerState.Foreign)
@@ -409,8 +414,6 @@ private void PopulateExtensions()
409414
if (dictExtensions.TryGetValue(name.ToLower(), out e))
410415
{
411416
e.RecordPropertyHandler(handlerGuid, handlerChainedGuid);
412-
413-
e.IdentifyCurrentProfile();
414417
}
415418
}
416419
}

AssociationMessages/LocalizedMessages.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

AssociationMessages/LocalizedMessages.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,4 +279,7 @@
279279
<data name="AtLeastOneExtension" xml:space="preserve">
280280
<value>-add and -remove commands require at least one extension to be specified</value>
281281
</data>
282+
<data name="WindowsWontExtendHandler" xml:space="preserve">
283+
<value>Windows will not allow the handler for {0} to be extended. Using profile with Windows handler.</value>
284+
</data>
282285
</root>

CommandLine/FileMeta.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ int wmain(int argc, WCHAR* argv[])
4040
SwitchArg promptSwitch(L"p",L"prompt",L"After execution, prompt to continue", false);
4141
cmd.add(promptSwitch);
4242

43+
// Define Explorer view of meta data switch
44+
SwitchArg explorerSwitch(L"v", L"explorer", L"Export metadata thisExplorer sees", false);
45+
cmd.add(explorerSwitch);
46+
4347
// Define XML file name override
4448
ValueArg<wstring> xmlFileArg(L"x",L"xml",L"Name of XML file (only valid if one target file)",false,L"",L"file name");
4549
cmd.add( xmlFileArg );
@@ -83,6 +87,11 @@ int wmain(int argc, WCHAR* argv[])
8387
if (!exportSwitch.isSet())
8488
throw ArgException(L"-c can only be used with -e", L"console");
8589
}
90+
else if (explorerSwitch.isSet())
91+
{
92+
if (!exportSwitch.isSet())
93+
throw ArgException(L"-v can only be used with -e", L"explorer");
94+
}
8695

8796
for (auto pos = targetFiles.begin(); pos != targetFiles.end(); ++pos)
8897
{
@@ -94,9 +103,11 @@ int wmain(int argc, WCHAR* argv[])
94103
result = ERROR_FILE_NOT_FOUND;
95104
break;
96105
}
97-
else if (!checker.HasOurPropertyHandler(targetFile))
106+
else if (0 == checker.HasPropertyHandler(targetFile) ||
107+
(-1 == checker.HasPropertyHandler(targetFile) && !explorerSwitch.isSet()))
98108
{
99-
// Skip files that do not have our property handler
109+
// Skip files that do not have our property handler,
110+
// unless we were asked for the Explorer view
100111
continue;
101112
}
102113
else if (deleteSwitch.isSet())
@@ -139,7 +150,7 @@ int wmain(int argc, WCHAR* argv[])
139150
if (exportSwitch.isSet())
140151
{
141152
xml_document<WCHAR> doc;
142-
ExportMetadata(&doc, targetFile);
153+
ExportMetadata(&doc, targetFile, explorerSwitch.isSet());
143154

144155
// writing to a string rather than directly to the stream is odd, but writing directly does not compile
145156
// (trying to access a private constructor on traits - a typically arcane template issue)

0 commit comments

Comments
 (0)