Have to put an icon on file extensions of a program on Windows explorer



  • Hi all,

    Here is the program from the book "C++-GUI-Programming-with-Qt-4-2nd Edition".

    I want to modify the code so that when that program is being used on any Windows machine it puts an icon on its stored files on Windows Explorer.

    I sought the Web and was told that I should change the code of both the script file and also the program.

    Here is the script file's content:

    function Component()
    {
        // default constructor
    }
    
    Component.prototype.createOperations = function()
    {
        // call the base create operations function
         component.createOperations();
          if (installer.value("os") == "win")
            {
               var userProfile = installer.environmentVariable("USERPROFILE");
               installer.setValue("UserProfile", userProfile);
    
               component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
               "@UserProfile@/Desktop/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@",
               "iconPath=@TargetDir@/Spreadsheet.ico");
    
               component.addOperation("CreateShortcut", "@TargetDir@/QSpreadsheet.exe",
               "@TargetDir@/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@", "iconPath=@TargetDir@/Spreadsheet.ico");
    
               component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
               "@StartMenuDir@/Spreadsheet.lnk", "workingDirectory=@TargetDir@", "iconPath=@TargetDir@/Spreadsheet.ico");
    
               component.addOperation("RegisterFileType", "sp",
               "@TargetDir@\\Spreadsheet.exe \" %1\"", "Tomy Inc",
               "application/x-binary", "@TargetDir@/Spreadsheet.ico",
               "ProgId=Spreadsheet.sp");
            }
     }
    

    And use QSettings.

    void MainWindow::readSettings()
    {
        QSettings settings("Tomy Inc.", "Spreadsheet");
        restoreGeometry(settings.value("geometry").toByteArray());
        recentFiles = settings.value("recentFiles").toStringList();
        updateRecentFileActions();
        bool showGrid = settings.value("showGrid", true).toBool();
        showGridAction->setChecked(showGrid);
        bool autoRecalc = settings.value("autoRecalc", true).toBool();
        autoRecalcAction->setChecked(autoRecalc);
    
        QSettings s("HKEY_CURRENT_USER\\SOFTWARE\\CLASSES",
                    QSettings::NativeFormat);
        s.setValue(".sp/DefaultIcon/.", QDir::toNativeSeparators
                   (qApp->applicationFilePath()));
        s.setValue(".sp/.","Tomy Inc.Spreadsheet.v1");
        s.setValue("Tomy Inc.Spreadsheet.v1/shell/open/command/.",
         QDir::toNativeSeparators(qApp->applicationFilePath()) + " %1");
    }
    
    //*************************************************************
    
    void MainWindow::writeSettings()
    {
        QSettings settings("Tomy Inc.", "Spreadsheet");
        settings.setValue("geometry", saveGeometry());
        settings.setValue("recentFiles", recentFiles);
        settings.setValue("showGrid", showGridAction->isChecked());
        settings.setValue("autoRecalc", autoRecalcAction->isChecked());
    
        QSettings s("HKEY_CURRENT_USER\\SOFTWARE\\CLASSES",
                    QSettings::NativeFormat);
        s.setValue(".sp/DefaultIcon/.", QDir::toNativeSeparators
                   (qApp->applicationFilePath()));
        s.setValue(".sp/.","Tomy Inc.Spreadsheet.v1");
        s.setValue("Tomy Inc.Spreadsheet.v1/shell/open/command/.",
         QDir::toNativeSeparators(qApp->applicationFilePath()) + " %1");
    }
    

    There is/are obviously some error(s). When I install the program on Windows no icons will be stored on the files I save on Windows Explorer.

    PS: I do not want to modify my registery to do the job; I want to have the program do that on "any" windows machine it will be installed on.

    One question here: How do you create applications?


  • Moderators

    @tomy said in Have to put an icon on file extensions of a program on Windows explorer:

    I do not want to modify my registery to do the job

    But then how do you want to achieve this? Registry is the way to do this on Windows.



  • @jsulm

    But then how do you want to achieve this? Registry is the way to do this on Windows.

    I meant I don't want to manipulate the registery directly; I want to manipulate the registery "inside the code" so that the code/program itself manipulates the registery in every Windows machine it will be installing on.

    For example you install a word processing application on Windows. You run the app and create a new file, put some data into it, and finally save it under the name "myfile_1" on Desktop. Doesn't that file have an icon on itself? It always has. Because the developer of that application has written the code so that it makes the Windows put some nice icon on the files that will be stored on Windows Explorer. I need this.
    Could I convey the issue correctly?


  • Moderators

    @tomy Then I don't understand the problem: you already have the code, right? Just execute it when your app is starting. Additionaly you could first check whether you already did this before if not then execute that code.


  • Moderators



  • @jsulm

    Then I don't understand the problem: you already have the code, right?

    Right.

    Just execute it when your app is starting. Additionaly you could first check whether you already did this before if not then execute that code.

    It's now me that doesn't understand!

    Please listen again, I want some nice icon to be put on the files I save.

    0_1505723830163_Capture.PNG

    Here is a file of MS Excel, and one of MS Word. As they are shown they have nice icon/shape/design on themselves but four files of the app we are talking about have no icon/shape/design on themselves, as if they are not known by Windows.


  • Moderators

    @tomy I know what you want to do. On windows this is done via registry, so out of scope of Qt. You need to find out what exactly you need to change in Windows registry. See MSDN.
    The icon is associated with a file type/extension.



  • @tomy
    Windows registers icons for file types only by extensions, it doesn't check byte sequences in files' headers like Linux.
    Thus, use WinAPI to edit registry from code on the installation stage of your program. Embed this functionality directly to the application - it's a bad practise. Only an installator must take responsibility for application's integration with a system.


  • Moderators

    I recently answered similar question: How to set icon for a custom type of file using Qt.

    As @Tikani mentioned this should probably be done in the installation stage, i.e. in some code invoked by the installer if it doesn't provide that functionality out of the box. Assuming, of course, that you have an installer in the first place.



  • @tomy from those Code-Example I'm guessing you're using the QtInstallerFramework to install your App on the window PC.

    In that case here in the official example from the docu on how to register fule extensions with Windows :-)



  • @Chris-Kawa

    I recently answered similar question: How to set icon for a custom type of file using Qt.

    Glad to hear that. I will go for reading it precisely.

    As @Tikani mentioned this should probably be done in the installation stage

    Yes, exactly.

    , i.e. in some code invoked by the installer if it doesn't provide that functionality out of the box. Assuming, of course, that you have an installer in the first place.

    I use the Qt Installer Framework 2.1 (QtIFW2.0.1) for making an installer.



  • This post is deleted!


  • @Chris-Kawa
    I included "shlobj.h" and used QSettings as below in the two functions where QSettings was previously used (because I was not sure where to use your code in):

    void MainWindow::readSettings()
    {
        QSettings settings("Tomy Inc.", "Spreadsheet");
        restoreGeometry(settings.value("geometry").toByteArray());
        recentFiles = settings.value("recentFiles").toStringList();
        updateRecentFileActions();
        bool showGrid = settings.value("showGrid", true).toBool();
        showGridAction->setChecked(showGrid);
        bool autoRecalc = settings.value("autoRecalc", true).toBool();
        autoRecalcAction->setChecked(autoRecalc);
    
        QSettings reg("HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\.sp\\DefaultIcon",
                      QSettings::NativeFormat);
        reg.setValue("Default", "C:\\Users\\Tomy\\Desktop\\package_directory"
                     "\\packages\\com.vendor.product\\data\\Spreadsheet.ico");
    
        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
    }
    
    //*************************************************************
    
    void MainWindow::writeSettings()
    {
        QSettings settings("Tomy Inc.", "Spreadsheet");
        settings.setValue("geometry", saveGeometry());
        settings.setValue("recentFiles", recentFiles);
        settings.setValue("showGrid", showGridAction->isChecked());
        settings.setValue("autoRecalc", autoRecalcAction->isChecked());
    
       QSettings reg("HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\.sp\\DefaultIcon",
                     QSettings::NativeFormat);
       reg.setValue("Default", "C:\\Users\\Tomy\\Desktop\\package_directory"
                   "\\packages\\com.vendor.product\\data\\Spreadsheet.ico");
    
       SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
    }
    

    Then changed the installscript.qs file to its previous status:

    function Component()
    {
        // default constructor
    }
    
    Component.prototype.createOperations = function()
    {
        // call the base create operations function
         component.createOperations();
          if (installer.value("os") == "win")
            {
               var userProfile = installer.environmentVariable("USERPROFILE");
               installer.setValue("UserProfile", userProfile);
    
               component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
               "@UserProfile@/Desktop/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@",
               "iconPath=@TargetDir@/Spreadsheet.ico");
    
               component.addOperation("CreateShortcut", "@TargetDir@/QSpreadsheet.exe",
               "@TargetDir@/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@", "iconPath=@TargetDir@/Spreadsheet.ico");
    
               component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
               "@StartMenuDir@/Spreadsheet.lnk", "workingDirectory=@TargetDir@", "iconPath=@TargetDir@/Spreadsheet.ico");
            }
     }
    

    Then created an installer and installed it on Windows. But unfortunately no icon was set!!

    Should I add this code to the script file too?



  • No any further help?? :(


  • Qt Champions 2016

    @tomy
    Hi
    you need to have more patience
    Mr Chris-Kawa is not online that often.



  • I this time removed the code:

    QSettings reg("HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\.sp\\DefaultIcon",
                      QSettings::NativeFormat);
        reg.setValue("Default", "C:\\Users\\Tomy\\Desktop\\Spreadsheet.ico");
    
        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
    

    from both void MainWindow::readSettings() and void MainWindow::writeSettings() functions and added it to the MainWindow's constructor body, then re-did the rest and installed that new on my Windows.
    Again, no changes in result!!
    There might be a problem either in the code or the script file.



  • I also tried to use this script file instead of the prior one. Created an installer using Qt Installer Framework but when installing the program faced en error.

    0_1505844874777_1.PNG

    I don't know what the right way is to do the job. Is it using that code? Or the script file? Or both of them? And what is the correct method having instructions to make the task done?


  • Moderators

    @tomy Correct way would be to search on MSDN as this is Windows only thing and not really related to Qt.



  • @jsulm
    I think MSDN talks about setting the icon on my Windows. So if I send the application for you, you too should set the icon on your Windows, and if we send the application for a hundred people, those a hundred plus we two should set the icon and so on.
    But if we set the icon using some method by the installation stage of the program, the program "itself" sets the icon for each and every user. No need to that 102 (or any number) persons set the icon themselves (apart from having good knowledge to be able to use registery that some users might not have).
    If you disagree please tell me your opinion.


  • Moderators

    @tomy Wrong. Why do you think so?
    You need to use win32 API to achieve what you want in your app, so your app can set the icon by itself (and not the user!) or the installer.
    win32 API is not Qt that's why I suggest to look at MSDN to find out how ann app or installer can do this.
    MSDN is for developers not users by the way.



  • @jsulm

    Wrong. Why do you think so?
    You need to use win32 API to achieve what you want in your app, so your app can set the icon by itself (and not the user!) or the installer.
    win32 API is not Qt that's why I suggest to look at MSDN to find out how ann app or installer can do this.
    MSDN is for developers not users by the way.

    But I even don't know what win32 API is and haven't been using it or MSDN up to now. Should I post a question there like a forum or how?

    By the way, thanks for your info.


  • Moderators

    @tomy Actually you probably do not have to use win32 API for this as you can access the Windows registry via QSettings for example. But you need to know what to change in Windows registry and that is something out of Qts scope (and I don't know either). You can search on the internet or ask on MSDN or any other Windows development related forum/mailing list.



  • @jsulm

    Actually you probably do not have to use win32 API for this as you can access the Windows registry via QSettings for example. But you need to know what to change in Windows registry and that is something out of Qts scope

    I fear if I post the question on MSDN they say "it's not completely related to Windows and ask a Qt forum"! :)

    Windows development related forum/mailing list.

    How to gain assistance from Qt mailing list?


  • Moderators

    @tomy said in Have to put an icon on file extensions of a program on Windows explorer:

    I fear if I post the question on MSDN they say "it's not completely related to Windows and ask a Qt forum"! :)

    Why? You even don't have to mention Qt at all. Just ask how to assign an icon to a file type (you can mention that you're using C++). It is completely related to Windows as Registry is a Windows only thing.

    I would not ask on Qt mailing list as it is more for people developing Qt.



  • @jsulm actually here is a detailed description on how to modify the Regestry to Assign a Custom Icon to a File Type



  • So I think I should use something like that: (My extension is .sp)
    Step1:

    QSettings reg("HKEY_CLASSES_ROOT\.sp\DefaultIcon", QSettings::NativeFormat);
    

    Step2:

      reg.setValue("Default", "C:\Users\Tomy\Desktop\Spreadsheet.ico");
    

    Step3:

    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
    

    It's simplified compared to the previous one.


  • Qt Champions 2016

    @tomy said in Have to put an icon on file extensions of a program on Windows explorer:

    C:\Users\Tomy\Desktop\Spreadsheet.ico

    That path would only be valid on your machine , would it not?

    Else the next user should also be Tomy :)



  • @mrjj
    I thought when I run the application, it copies that icon from that path and embeds it into the Spreadsheet.exe file which then will be used for creating the installer, making the other users not need to have such an icon on their Windows machines. :(

    If it's not right, so what path should I put that icon and gives the statement its path?


  • Moderators

    Wow, a lot of confusion here and the problem is actually pretty simple. There are 3 questions: the what, the where and the how.

    The what - what you need to do on Windows to set an icon for a file type.
    You need to set a registry key for that: HKEY_CURRENT_USER\\SOFTWARE\\Classes\\.mp5\\DefaultIcon and then force Windows to update icon cache.
    Do not write to HKEY_CLASSES_ROOT directly.

    The where - where to do it.
    You can do it in couple of places:
    In your app when it starts. Generally that's not a good idea as it's annoying that the app changes files settings every time it runs. A user might want to change that manually later and you should respect that.
    The better place for that is the app's installer and depending on the installer you use it might give you that functionality or you'd need to do it yourself.

    The how - how to do it
    If you decide that your app should register the icon use the code that's been posted several times now:

    QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Classes\\.sp\\DefaultIcon", QSettings::NativeFormat);
    reg.setValue("Default", "C:\\Path\\To\\some_icon.ico");
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
    

    As @mrjj noted - don't hardcode that path. Use some runtime detection of where it is located. There's no magic like automatic copying or embedding involved. The icon needs to be deployed to the user's machine along the executable.
    You can also embed the icon in the executable manually and use that as the registry value. The syntax is a little different then.
    If you want that say so and I'll tell you how.

    If you decide that the installer should do that and you choose Qt installer framework the feature is provided by it. No need for c++ or winapi. Follow the example. In the Component.prototype.createOperations function add built-in operation "RegisterFileType"

    component.addOperation("RegisterFileType",
                           "sp",
                           "@TargetDir@\\Spreadsheet.exe \" %1\"",
                           "Tomy file",
                           "some MIME type",
                           "@TargetDir@/some_icon.ico",
                           "ProgId=Whatever.");
    

    Adjust the strings of course. Again - no magic involved. The icon needs to be deployed along with the executable.



  • @Chris-Kawa

    If you decide that the installer should do that and you choose Qt installer framework the feature is provided by it. No need for c++ or winapi. Follow the example. In the Component.prototype.createOperations function add built-in operation "RegisterFileType"

    component.addOperation("RegisterFileType",
                           "sp",
                           "@TargetDir@\\Spreadsheet.exe \" %1\"",
                           "Tomy file",
                           "some MIME type",
                           "@TargetDir@/some_icon.ico",
                           "ProgId=Whatever.");
    

    Adjust the strings of course. Again - no magic involved. The icon needs to be deployed along with the executable.

    Thanks.
    I this it's better among others.

    It's code of the script file I've used so far:

    function Component(){ }
    
    Component.prototype.createOperations = function()
    {
       component.createOperations();
        if (installer.value("os") == "win")
          {
            var userProfile = installer.environmentVariable("USERPROFILE");
            installer.setValue("UserProfile", userProfile);
    
            component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
            "@UserProfile@/Desktop/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@",
            "iconPath=@TargetDir@/Spreadsheet.ico");
    
            component.addOperation("CreateShortcut", "@TargetDir@/QSpreadsheet.exe",
            "@TargetDir@/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@",
            "iconPath=@TargetDir@/Spreadsheet.ico");
    
            component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
            "@StartMenuDir@/Spreadsheet.lnk", "workingDirectory=@TargetDir@",
            "iconPath=@TargetDir@/Spreadsheet.ico");
          }
     }
    

    I have two issues:
    1- should I use the whole code of that link and embed it into this, or is it enough to add the operation below into it?

    component.addOperation("RegisterFileType",
                           "sp",
                           "@TargetDir@\\Spreadsheet.exe \" %1\"",
                           "Spreadsheet",
                           "some MIME type",
                           "@TargetDir@/Spreadsheet.ico",
                           "ProgId=Whatever.")
    

    2- What's that MIME file?
    Is that operation OK to use now?


  • Moderators

    should I use the whole code (...)

    Well no, the example is just that - an example. It creates a ui (a checkbox) for the user to choose if he wants to set the icon and then randomly generates some extension.
    You don't need all that. Of interest to you is just the function that actually sets the thing. that's the component.addOperation(...) part.

    MIME type is an identifier that globally describes your file type. See here. If it's your custom type you need to invent some new MIME type e.g. "application/tomy".

    As for ProgId that's a bit more involved. I previously said that to set an icon you need to change that HKEY_CURRENT_USER\\SOFTWARE\\Classes\\.mp5\\DefaultIcon key.
    Well that's a shortcut actually, as the "full" setup for this is to create two keys:
    HKEY_CURRENT_USER\\SOFTWARE\\Classes\\SomeAppName.SomeType\DefaultIcon
    and then refer to that name in the
    HKEY_CURRENT_USER\\SOFTWARE\\Classes\\.sp key by the name SomeAppName.SomeType.
    It does the same thing but is better if you need more settings than just the file icon.
    Qt Installer Framework uses that "full" setup, so the ProgId is that "SomeAppName.SomeType" identifier.



  • @Chris-Kawa
    Thanks.
    So what I need I think is:
    First, remove the QSettings code inside the program's code. Because I want to use the script file for the task not the program's code.
    Second, use the script file with this code to create the installer:

    function Component(){ }
    
    Component.prototype.createOperations = function()
    {
       component.createOperations();
        if (installer.value("os") == "win")
          {
            var userProfile = installer.environmentVariable("USERPROFILE");
            installer.setValue("UserProfile", userProfile);
    
            component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
            "@UserProfile@/Desktop/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@",
            "iconPath=@TargetDir@/Spreadsheet.ico");
    
            component.addOperation("CreateShortcut", "@TargetDir@/QSpreadsheet.exe",
            "@TargetDir@/Spreadsheet.lnk" ,"workingDirectory=@TargetDir@",
            "iconPath=@TargetDir@/Spreadsheet.ico");
    
            component.addOperation("CreateShortcut", "@TargetDir@/Spreadsheet.exe",
            "@StartMenuDir@/Spreadsheet.lnk", "workingDirectory=@TargetDir@",
            "iconPath=@TargetDir@/Spreadsheet.ico");
            
            component.addOperation("RegisterFileType",
                           "sp",
                           "@TargetDir@\\Spreadsheet.exe \" %1\"",
                           "Spreadsheet",
                           "Spreadsheet/sp",
                           "@TargetDir@/Spreadsheet.ico",
                           "ProgId=Spreadsheet.sp")
          }
     }
    
    
    

    Do you agree?
    I tested it and result is the same still!



  • I don't know what part of the code is still incorrect.



  • I might create a new thread focusing exactly at this point at the beginning, which seems to be the source of my issue.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.