FPiette 383 Posted March 5, 2023 I wrote a simple shell property sheet handler using Delphi 11. It works perfectly when I register it for all file types. But it does nothing (My property page not shown) when I Register it for a specific file type. Registration for all file types: // Error handling removed for clarity Reg := TRegistry.Create; Reg.RootKey := HKEY_CLASSES_ROOT; if Reg.OpenKey('\*\ShellEx\PropertySheetHandlers\MyDelphiPropSheetHandler', True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; ClassID is registered as part of the COM server. This is done automatically by Delphi RTL. Registering for a single file type: I simply replace "*" in the above code by the file extension which is ".arw" in my case: // Error handling removed for clarity Reg := TRegistry.Create; Reg.RootKey := HKEY_CLASSES_ROOT; if Reg.OpenKey('\.arw\ShellEx\PropertySheetHandlers\MyDelphiPropSheetHandler', True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; This simply does nothing. No error, just nothing. The key is correctly written in the registry. What am I doing wrong? Share this post Link to post
programmerdelphi2k 237 Posted March 5, 2023 (edited) my fault sorry... Edited March 6, 2023 by programmerdelphi2k Share this post Link to post
Remy Lebeau 1396 Posted March 5, 2023 (edited) 1 hour ago, FPiette said: Registering for a single file type: I simply replace "*" in the above code by the file extension which is ".arw" in my case: Per the documentation, Registering Shell Extension Handlers, you need to create your ShellEx subkey underneath the extension's ProgID subkey, not under the extension subkey, eg: HKEY_CLASSES_ROOT .arw (Default) = <MyArwProgID> MyArwProgID ShellEx PropertySheetHandlers MyDelphiPropSheetHandler (Default) = <MyClassID> For example: Reg := TRegistry.Create; try Reg.RootKey := HKEY_CLASSES_ROOT; if Reg.OpenKeyReadOnly('\.arw') then begin ProgID := Reg.ReadString(''); Reg.CloseKey; Reg.Access := KEY_CREATE_SUB_KEY or KEY_SET_VALUE; if (ProgID <> '') and Reg.OpenKey('\' + ProgID + '\ShellEx\PropertySheetHandlers\MyDelphiPropSheetHandler', True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; end; end; finally Reg.Free; end; Note that you really should not be writing directly to HKEY_CLASSES_ROOT, you should be writing to either HKEY_LOCAL_MACHINE\Software\Classes\ or HKEY_CURRENT_USER\Software\Classes\ instead, per the documentation, HKEY_CLASSES_ROOT Key: Quote To change the settings for the interactive user, store the changes under HKEY_CURRENT_USER\Software\Classes rather than HKEY_CLASSES_ROOT. To change the default settings, store the changes under HKEY_LOCAL_MACHINE\Software\Classes. If you write keys to a key under HKEY_CLASSES_ROOT, the system stores the information under HKEY_LOCAL_MACHINE\Software\Classes. If you write values to a key under HKEY_CLASSES_ROOT, and the key already exists under HKEY_CURRENT_USER\Software\Classes, the system will store the information there instead of under HKEY_LOCAL_MACHINE\Software\Classes. For instance: Reg := TRegistry.Create; try if ForTheCurrentUserOnly then Reg.RootKey := HKEY_CURRENT_USER else Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKeyReadOnly('\Software\Classes\.arw') then begin ProgID := Reg.ReadString(''); Reg.CloseKey; Reg.Access := KEY_CREATE_SUB_KEY or KEY_SET_VALUE; if (ProgID <> '') and Reg.OpenKey('\Software\Classes\' + ProgID + '\ShellEx\PropertySheetHandlers\MyDelphiPropSheetHandler', True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; end; end; finally Reg.Free; end; Edited March 5, 2023 by Remy Lebeau Share this post Link to post
FPiette 383 Posted March 6, 2023 I dont know if help you, but you try see the result when associating any new EXTention file Doesn't help. My question is not related to the creation of a new file type (extension). Share this post Link to post
FPiette 383 Posted March 6, 2023 (edited) 12 hours ago, Remy Lebeau said: Per the documentation, Registering Shell Extension Handlers, you need to create your ShellEx subkey underneath the extension's ProgID subkey, not under the extension subkey Thanks. I already saw that documentation. It is probably outdated because there is no ProgID for the file extension. Edited March 6, 2023 by FPiette Share this post Link to post
programmerdelphi2k 237 Posted March 6, 2023 (edited) my fault.... sorry! Edited March 6, 2023 by programmerdelphi2k Share this post Link to post
FPiette 383 Posted March 6, 2023 39 minutes ago, programmerdelphi2k said: well, here in my test it at least works... Thank you for your message but you are still out of scope of my question! My question about a property sheet handler associated with a file extension. To say it otherwise, I don't want to create a new file extension nor associate a program to open a file having that file extension (This is what you show). I guess that you don't know what a "Property Sheet Handler" is. A property sheet is what you see when you right-click on a file and select "properties" in the popup menu. Then you see a window with several tabs. A property sheet is a shell extension that build a show a new tab. I wrote such a property sheet handler using Delphi. I can easily register it for ALL file types (AKA file extension) but I don't want it for all files, I want it only for a small number of file extensions. Look at this: https://learn.microsoft.com/en-us/windows/win32/shell/propsheet-handlers Share this post Link to post
programmerdelphi2k 237 Posted March 6, 2023 (edited) @FPiette sorry ... really I think that was about association... 😁 ok, im get out here sorry again... im not has intimity with "prop-sheets" at all Edited March 6, 2023 by programmerdelphi2k Share this post Link to post
Brian Evans 105 Posted March 6, 2023 Putting keys in HKEY_CLASSES_ROOT (HKCR) from a program has a bunch of extra caveats/oddities (HKEY_CLASSES_ROOT Key - Win32 apps | Microsoft Learn). Would suggest putting keys directly in the appropriate spots in HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER. Share this post Link to post
Remy Lebeau 1396 Posted March 6, 2023 (edited) 9 hours ago, FPiette said: Thanks. I already saw that documentation. It is probably outdated because there is no ProgID for the file extension. It is not outdated. I've used that info in the past to create shell handlers, and my machines have many 3rd party property sheet handlers installed that are registered the way I described. If your extension does not have a ProgID assigned to it (why?), you need to create and assign your own ProgID, even if just to create the Shellex subkey. All extension subkeys should have a ProgID subkey associated with them, because it is the ProgID subkey that contains the actual commands that tell Windows what to do with files that have those extensions. Multiple file extensions that share a common set of commands can be assigned to a single ProgID. Edited March 6, 2023 by Remy Lebeau Share this post Link to post
FPiette 383 Posted March 6, 2023 5 minutes ago, Remy Lebeau said: All extension subkeys should have a ProgID subkey associated with them, because it is the ProgID subkey that contains the commands that tell the OS what to do with the file. There is no ProgID for the file type. And yet, Windows open it using Microsoft Windows Photo application (.ARW file is Sony RAW file for photo). If you want to try with a real file on your system, you can download a photo in .ARW format: http://wiki.overbyte.be/arch/FPI09894.ARW (Can't attach it here because it is a 50MB). It is possible that you need to install Microsoft RAW extension (https://apps.microsoft.com/store/detail/extension-dimage-raw/9NCTDW2W1BH8) It is needed for Win10 but already included by default with Win11. If you have Adobe Photoshop or Lightroom installed, thing may be different, not sure. Share this post Link to post
programmerdelphi2k 237 Posted March 6, 2023 (edited) sorry (im trying undertand more about)... { GUID } -> is it appointing to real exe/dll/ocx app = ProgID HKEYS_CLASSES_ROOT == * (all files) MSWin10 has this {C7657C4A-9F68-40fa-A4DF-96BC08EB3551} by default "Photo Thumbnail Provider" Edited March 6, 2023 by programmerdelphi2k Share this post Link to post
Remy Lebeau 1396 Posted March 6, 2023 (edited) 4 hours ago, FPiette said: There is no ProgID for the file type. And yet, Windows open it using Microsoft Windows Photo application (.ARW file is Sony RAW file for photo). That is because the extension key has a Thumbnail Provider registered, which is registered via a "ShellEx" subkey on the extension key itself. However, since you want to register a Property Sheet handler instead, the extension key must have a ProgID registered with it, as I showed earlier. Why are you being so hesitant to define one? If you want your Property Sheet Handler to work correctly, you don't have a choice, this is simply how Property Sheet Handlers operate. A "ShellEx" subkey on the extension key itself will not work for a Property Sheet Handler. Edited March 6, 2023 by Remy Lebeau Share this post Link to post
David Heffernan 2345 Posted March 7, 2023 Reluctance to simply follow the documented steps is very odd here. Share this post Link to post
FPiette 383 Posted March 7, 2023 10 hours ago, Remy Lebeau said: the extension key must have a ProgID registered with it, as I showed earlier. Why are you being so hesitant to define one? Because I don't know what to make it point to. You told that the ProgId is used when the file must be opened. I don't want to change the application opening it. My code is just to display some metadata from the file, not the file data. Share this post Link to post
Remy Lebeau 1396 Posted March 7, 2023 (edited) 16 hours ago, FPiette said: Because I don't know what to make it point to. You told that the ProgId is used when the file must be opened. I don't want to change the application opening it. My code is just to display some metadata from the file, not the file data. Creating a new ProgID just to define a PropertySheetHandler will not change which app opens the file type. Those are separate operations. Besides, in your example, there is no app registered for the file type anyway, since there is no ProgID registered for the file type (the file is being opened through a separately registered Thumbnail Handler instead). Simply don't create any Shell commands inside of your new ProgID, only register your ShellEx handler. Edited March 8, 2023 by Remy Lebeau Share this post Link to post
FPiette 383 Posted March 7, 2023 Thanks @Remy Lebeau. I will give it a try. Share this post Link to post
FPiette 383 Posted March 9, 2023 On 3/5/2023 at 7:27 PM, Remy Lebeau said: Per the documentation, Registering Shell Extension Handlers, you need to create your ShellEx subkey underneath the extension's ProgID subkey, not under the extension subkey I have coded that. And it doesn't work at all. Here is the code: procedure TMyDelphiPropSheetHandlerFactory.UpdateRegistry(ARegister: Boolean); const Key = '*\shellex\PropertySheetHandlers\'; var Reg : TRegistry; ClassID : String; {$IFDEF REG_FILENAME} ProgNameVer : String; RegExtArray : TStringDynArray; Ext : String; ExtCount : Integer; ProgID : String; ProgTmp : String; {$ENDIF} begin ClassID := GUIDToString(Class_MyDelphiPropSheetHandler); Reg := TRegistry.Create; try {$IFDEF REG_FILENAME} if ARegister then inherited UpdateRegistry(ARegister); Reg.RootKey := HKEY_LOCAL_MACHINE; ProgNameVer := ProgName + '.' + ProgVer; RegExtArray := SplitString(RegFileExts, ';'); if ARegister then begin // Register if Length(RegExtArray) > 0 then begin ExtCount := 0; for Ext in RegExtArray do begin if not Reg.OpenKey('\Software\Classes\' + Ext, FALSE) then MyMessageBox('File type "%s" doesn''t exists', [Ext]) else begin Inc(ExtCount); ProgID := Reg.ReadString(''); if ProgID = '' then begin ProgID := ProgNameVer; Reg.WriteString('', ProgID); MyMessageBox('Created ProgID[%s]="%s"', [Ext, ProgID]); Reg.CloseKey; if not Reg.OpenKey('\Software\Classes\' + ProgID, FALSE) then begin Reg.OpenKey('\Software\Classes\' + ProgID, TRUE); MyMessageBox('Created key "\Software\Classes\%s"', [ProgID]); end; end else MyMessageBox('ProgID[%s]="%s"', [Ext, ProgID]); Reg.CloseKey; if ExtCount = 1 then begin if Reg.OpenKey('\Software\Classes\' + ProgID + '\ShellEx\PropertySheetHandlers\' + ProgName, TRUE) then begin Reg.WriteString('', ClassID); Reg.CloseKey; MyMessageBox('PropertySheetHandlers[%s]="%s"', [Ext, ProgName]); end; end; MyMessageBox('Registered for Ext="%s"', [Ext]); SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil); end; end; end; end else begin // Unregister ProgID := ProgName + '.' + ProgVer; if Length(RegExtArray) > 0 then begin for Ext in RegExtArray do begin if Reg.OpenKey('\Software\Classes\' + Ext, FALSE) then begin ProgTmp := Reg.ReadString(''); if SameText(ProgID, ProgTmp) then begin Reg.DeleteValue(''); MyMessageBox('Deleted ProgID="%s" for Ext="%s"', [ProgID, Ext]); end; end; end; end; if Reg.OpenKey('\Software\Classes\' + ProgNameVer, False) then Reg.DeleteKey('\Software\Classes\' + ProgNameVer); inherited UpdateRegistry(ARegister); end; {$ELSE} inherited UpdateRegistry(ARegister); Reg.RootKey := HKEY_CLASSES_ROOT; if ARegister then begin if Reg.OpenKey('\*\ShellEx\PropertySheetHandlers\' + ProgName, True) then Reg.WriteString('', ClassID) else MyMessageBox('UpdateRegistry : Can''t open registry key!'); end else begin if Reg.OpenKey('\*\ShellEx\PropertySheetHandlers\' + ProgName, False) then begin if not Reg.DeleteKey('\*\ShellEx\PropertySheetHandlers\' + ProgName) then MyMessageBox('Registry key *NOT* deleted!'); end; end; {$ENDIF} finally Reg.CloseKey; Reg.Free; end; end; As you can see, the symbol REG_FILENAME which select registration for all files or single file name extension. MyMessageBox is a message box that I use for debugging. The registration for all file name extension work nicely, so I'm sure the property sheet code is correct. Only the registration for a single extension doesn't work (no error, just do nothing). The complete source code is there: MyDelphiPropSheetHandlerSourceCode.zip There is also an image in .arw file format to test: http://wiki.overbyte.be/arch/FPI09894.ARW BTW: The property sheet does nothing except displaying the selected files in a TMemo. Share this post Link to post
David Heffernan 2345 Posted March 9, 2023 Rather than writing code, just edit the registry manually to debug your problem. Once you know what the solution is, then write code. If you want somebody here to troubleshoot then showing registry structure is much easier to work through than Delphi code to write registry values. Share this post Link to post
FPiette 383 Posted March 9, 2023 1 hour ago, David Heffernan said: Rather than writing code, just edit the registry manually to debug your problem. showing registry structure is much easier to work through than Delphi code to write registry values. The registry structure is what @Remy Lebeaushowed in his constructive answer and is conform to Microsoft documentation and yet it doesn't work. Using registry I checked that my code create the keys as specified by Microsoft (see below). Entering something manually? OK but what? That's the question. I miss something and I don't know what. If I don't publish code, someone (you for example) will say "Show what you tried...". That's why I copied relevant code here and made complete project available. Lines marked "<= Not me" was existing before. I write in HKLM\SOFTWARE\Classes and Windows automatically replicate the keys in HKCR. On my system, there is no property sheet that I can analyze and reproduce the structure. Share this post Link to post
Remy Lebeau 1396 Posted March 9, 2023 (edited) Quote The registration for all file name extension work nicely, so I'm sure the property sheet code is correct. Only the registration for a single extension doesn't work (no error, just do nothing). How would you know fore sure? You are not reporting every error that may occur. For example, if an extension does not have a ProgID yet, and you try to create a new ProgID but Reg.OpenKey() fails, you don't report that. Also, I see you creating your PropertySheetHandlers subkey only for the 1st extension in your array, because your code assumes that all extensions are using the same ProgID, which is not a guarantee, so you should be checking/creating the PropertySheetHandlers subkey for every ProgID you encounter. Try something more like this: procedure TMyDelphiPropSheetHandlerFactory.UpdateRegistry(ARegister: Boolean); var Reg : TRegistry; ClassID : String; Updated : Boolean; {$IFDEF REG_FILENAME} ProgNameVer : String; RegExtArray : TStringDynArray; Ext : String; ProgID : String; {$ENDIF} begin ClassID := GUIDToString(Class_MyDelphiPropSheetHandler); Updated := False; try Reg := TRegistry.Create; try {$IFDEF REG_FILENAME} Reg.RootKey := HKEY_LOCAL_MACHINE; ProgNameVer := ProgName + '.' + ProgVer; RegExtArray := SplitString(RegFileExts, ';'); if ARegister then begin inherited UpdateRegistry(ARegister); for Ext in RegExtArray do begin if not Reg.OpenKeyReadOnly('\Software\Classes\' + Ext) then begin if Reg.LastError = ERROR_FILE_NOT_FOUND then MyMessageBox('Ext="%s" doesn''t exist', [Ext]) else MyMessageBox('Can't open registry key for Ext="%s"', [Ext]); Continue; end; ProgID := Reg.ReadString(''); Reg.CloseKey; if ProgID = '' then begin ProgID := ProgNameVer; Reg.Access := KEY_SET_VALUE; if not Reg.OpenKey('\Software\Classes\' + Ext, True) then begin MyMessageBox('Can't open registry key to update Ext="%s"', [Ext]); Continue; end; Reg.WriteString('', ProgID); Reg.CloseKey; Updated := True; MyMessageBox('Created ProgID[%s]="%s"', [Ext, ProgID]); end else MyMessageBox('ProgID[%s]="%s"', [Ext, ProgID]); Reg.Access := KEY_SET_VALUE; if not Reg.OpenKey('\Software\Classes\' + ProgID + '\ShellEx\PropertySheetHandlers\' + ProgName, True) then begin MyMessageBox('Can''t create PropertySheetHandler="%s" for ProgID="%s"', [ProgName, ProgID]); Continue; end; Reg.WriteString('', ClassID); Reg.CloseKey; Updated := True; MyMessageBox('PropertySheetHandlers[%s]="%s"', [ProgID, ProgName]); MyMessageBox('Registered for Ext="%s"', [Ext]); end; end else begin for Ext in RegExtArray do begin if not Reg.OpenKeyReadOnly('\Software\Classes\' + Ext) then begin if Reg.LastError <> ERROR_FILE_NOT_FOUND then MyMessageBox('Can't open registry key for Ext="%s"', [Ext]); Continue; end; ProgID := Reg.ReadString(''); Reg.CloseKey; if SameText(ProgID, ProgNameVer) then begin Reg.Access := KEY_SET_VALUE; if not Reg.OpenKey('\Software\Classes\' + Ext, False) then begin MyMessageBox('Can't open registry key to update Ext="%s"', [Ext]); end else begin if Reg.DeleteValue('') then begin Reg.CloseKey; Updated := True; MyMessageBox('Deleted ProgID="%s" value for Ext="%s"', [ProgID, Ext]) end else begin Reg.CloseKey; MyMessageBox('Can''t delete ProgID="%s" value for Ext="%s"', [ProgID, Ext]); end; end; end; if ProgID <> '' then begin Reg.Access := KEY_SET_VALUE or _DELETE; if not Reg.OpenKey('\Software\Classes\' + ProgID + '\ShellEx\PropertySheetHandlers\', False) then begin if Reg.LastError <> ERROR_FILE_NOT_FOUND then MyMessageBox('Can''t open PropertySheetHandlers key for ProgID="%s"', [ProgID]); end else if Reg.DeleteKey(ProgName) or (Reg.LastError = ERROR_FILE_NOT_FOUND) then begin Reg.CloseKey; Updated := True; MyMessageBox('Deleted PropertySheetHandler="%s" for ProgID="%s"', [ProgName, ProgID]); end else begin Reg.CloseKey; MyMessageBox('Can''t delete PropertySheetHandler="%s" for ProgID="%s"', [ProgName, ProgID]); end; end; end; if Reg.DeleteKey('\Software\Classes\' + ProgNameVer) or (Reg.LastError = ERROR_FILE_NOT_FOUND) then begin Updated := True; MyMessageBox('Deleted registry key for ProgID="%s"', [ProgNameVer]) end else MyMessageBox('Can''t delete registry key for ProgID="%s"', [ProgNameVer]); inherited UpdateRegistry(ARegister); end; {$ELSE} Reg.RootKey := HKEY_CLASSES_ROOT; if ARegister then begin inherited UpdateRegistry(ARegister); Reg.Access := KEY_SET_VALUE; if Reg.OpenKey('\*\shellex\PropertySheetHandlers\' + ProgName, True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; Updated := True; MyMessageBox('Created registry key for PropertySheetHandler="%s"', [ProgName]); end else MyMessageBox('Can''t create/open registry key for PropertySheetHandler="%s"', [ProgName]); end else begin if Reg.DeleteKey('\*\shellex\PropertySheetHandlers\' + ProgName) or (Reg.LastError = ERROR_FILE_NOT_FOUND) then begin Updated := True; MyMessageBox('Deleted registry key for PropertySheetHandler="%s"', [ProgName]); end else MyMessageBox('Can''t delete registry key for PropertySheetHandler="%s"', [ProgName]); inherited UpdateRegistry(ARegister); end; {$ENDIF} finally Reg.Free; end; finally if Updated then SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil); end; end; UPDATE: something else I just realized - you did not say whether you are running your code from a 32bit or 64bit executable. If 32bit on a 64bit system, you need to take WOW64 into account, by including the KEY_WOW64_32KEY or KEY_WOW64_64KEY flag in the TRegistry.Access property as needed. Edited March 9, 2023 by Remy Lebeau Share this post Link to post
Remy Lebeau 1396 Posted March 9, 2023 (edited) 36 minutes ago, FPiette said: If I don't publish code, someone (you for example) will say "Show what you tried...". That's why I copied relevant code here and made complete project available. Your PropertySheelHandler should not have its own ProgID assigned to it. You can assign your PropertySheetHandler to other ProgIDs as needed, but it should not have its own ProgID. So, get rid of the "HKLM\SOFTWARE\Classes\CLSID\{AF6B...}\ProgID" subkey. Edited March 9, 2023 by Remy Lebeau Share this post Link to post
Anders Melander 1783 Posted March 9, 2023 Works for me: I'll clean up the code and post it later... Share this post Link to post
FPiette 383 Posted March 9, 2023 1 hour ago, Remy Lebeau said: because your code assumes that all extensions are using the same ProgID First thing first, I try with a single extension: .arw. Then I'll update the code when required for multiple extensions, that's not the issue. Share this post Link to post
FPiette 383 Posted March 9, 2023 35 minutes ago, Anders Melander said: Works for me: Wonderful. From the screen dump I cannot determine that the property sheet handler is registered for all extensions (That work here also) or for a single one (Don't work here). If REG_FILENAME symbol is defined, my code will register for a single file extension (.arw in my original code), that is what you need to try. 36 minutes ago, Anders Melander said: I'll clean up the code and post it later.. I will really be grateful for that code. Share this post Link to post