Announcement

Collapse
No announcement yet.

Need a smarter INI files update – please help

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Need a smarter INI files update – please help

    Greetings,

    I’ve been using v. 2.1.1007 for corporate PowerBuilder GUI deployments for a big while, and it almost never failed to fulfill my expectations. Unfortunately, this time it’s slightly different.

    I need to update the Keys in the [HOSTS] Section of several INI files (total of 7) on users PCs across the company. Updates are consistent and are not conditional. For example, the following

    Code:
    [HOSTS]
    Delivery=[B][COLOR="blue"]appmprod[/COLOR][/B]
    ……………
    should become this

    Code:
    [HOSTS]
    Delivery=[B][COLOR="darkgreen"]mmvprod[/COLOR][/B]
    ……………
    While this, of course, is pretty straight-forward thanks to the INI File Dialog, the real problem is that different users have different sets of the seven INI files. Some may have all of them but most just have a subset, and the files they have vary from one machine to another. That is because not everybody have access to all seven GUI apps, and usually just some of them.

    The behavior I'm observing is when an INI file is missing, MSI Factory just creates a new one on the fly with a single [HOSTS] section in it. A highly undesirable outcome. I expected MSI Factory to have a built-in smartness not to do anything in case the file could not be found, and process only the INIs that exist on a PC given.

    I understand I can write a custom LUA script where I would loop through all the INIs (the folder they reside in and its location is consistent across all the machines). But that seems to be an overkill - isn’t there a simpler way of doing that? Could it be that I’m just missing one of the features where I would check some kind of an “Overwrite Only When Exists” box? Perhaps that would be a newer MSI Factory version? Please advise.

    Any kind of help is greatly appreciated.
    Thank you very much in advance,
    Leon
    Last edited by e500; 02-01-2016, 01:13 PM.

  • #2
    Originally posted by e500 View Post
    The behavior I'm observing is when an INI file is missing, MSI Factory just creates a new one on the fly with a single [HOSTS] section in it. A highly undesirable outcome. I expected MSI Factory to have a built-in smartness not to do anything in case the file could not be found, and process only the INIs that exist on a PC given.
    Uhm, no, sorry - it looks like you got that backwards. It would be highly undesirable if MSI Factory would not do what is expected, and behaving the manner it does is exactly like it should be. Remember that MSI Factory is a graphical IDE for building Windows Installer packages with the aid of WiX. The way a package has to behave is dictated not by the graphical user interface, but by Windows Installer.

    If you check the documentation for the IniFile element in Wix, you will see that there is no attribute for "write a value only if file already exists". You have the contrary, though, so you can avoid overwriting a value if it already exists. Sadly, that isn't what you need, so you have to use a different approach.

    There is no need to do any scripting, as what you want can be done easily with the tools provided in Windows Installer. Perform a File Search (see Requirements - Searches - File Search), and create a Property (in upper case) which will tell you if the file you are looking for already exists on the target system. The Property will hold the path where the file was found, or it will contain the value you set as default. You can set where to search, if sub folders should be scanned, and the file name to look for.

    Click image for larger version

Name:	SCRN-2016-02-01-04.png
Views:	1
Size:	14.2 KB
ID:	284371

    Now go to your INI Files - Set INI Value entry. Go to the Component for that entry, and set a Condition on it. For example INIFILE_001 <> "NOT_FOUND", if "NOT_FOUND" was set as the default value.

    Click image for larger version

Name:	SCRN-2016-02-01-05.png
Views:	1
Size:	26.8 KB
ID:	284372

    Build and run with a verbose log. Look for entries like this:

    Code:
    INIFILE_001 = [highlight]NOT_FOUND[/highlight]
    (…)
    MSI (s) (D8:10) [23:50:47:460]: Component: INISet_0001; Installed: Absent;   Request: Local;   Action: [highlight]Null[/highlight]
    or

    Code:
    (…)
    MSI (c) (FC:2C) [23:56:58:712]: PROPERTY CHANGE: Modifying INIFILE_001 property. Its current value is 'NOT_FOUND'. Its new value: [highlight]'C:\Program Files (x86)\mytestfile.ini'[/highlight].
    (…)
    MSI (s) (D8:EC) [23:57:04:315]: Component: INISet_0001; Installed: Absent;   Request: Local;   Action: [highlight]Local[/highlight]
    The first entries were taken from a log where the INI file wasn't present in the folder, while the second example shows how the same setup sets the Property and installs the Component because the file was found.

    I believe that MSI Factory offers you everything you need. Give it a try.

    Ulrich

    Comment


    • #3
      Hello Ulrich,

      Many thanks for detailed instructions with graphics. I tried several times, however could not get it work. Could it be that File Search dialog only supports the files in system (predefined) folders, like in your example, and do not support custom ones? In my case, the full path is C:\Program Files (x86)\FT Interactive Data\Mips\Ini, which I fully constructed in the Folders dialog, giving the "Ini" folder ID "INI_FOLDER" (tried "INSTALLDIR" as well).

      Needless to say that the INI Files dialog works perfectly fine without File Search, i.e. the “Ini” folder does get recognized by the installer.

      Comment


      • #4
        Okay, I am not sure exactly what went wrong in your tests, because here it worked. Before you attempt to replicate this using the sample project, please see what I did:

        First, I set up the folders in this manner:

        Click image for larger version

Name:	SCRN-2016-02-02-03.png
Views:	1
Size:	6.6 KB
ID:	284373

        As you can see, I tried to replicate the folder structure you described in your post. Then I set up the search:

        Click image for larger version

Name:	SCRN-2016-02-02-04.png
Views:	1
Size:	14.1 KB
ID:	284374

        Then I created a sample INI file in my testing environment:

        Click image for larger version

Name:	SCRN-2016-02-02-01.png
Views:	1
Size:	17.8 KB
ID:	284375

        Then I processed the MSI, and I could see this:

        Click image for larger version

Name:	SCRN-2016-02-02-02.png
Views:	1
Size:	8.1 KB
ID:	284376

        Of course I already tested without having the initial INI file in place, in that case, no new file was created. So this part is definitely working well.

        Now, perhaps you allow your client to set a custom installation folder. In this case you may have a problem, because you would perform the search at the default/wrong location, and may not find the existing file. If you planned ahead and saved the installation folder of the product in the registry, either by using a custom key under SOFTWARE\Company\Product, or by setting the InstallLocation value of the MSI itself via the ARPINSTALLLOCATION Property, then you are in luck.

        Click image for larger version

Name:	SCRN-2016-02-03-01.png
Views:	1
Size:	12.0 KB
ID:	284377

        You would "correct" the location of the INSTALLDIR right at the start, so the [Folder_Ini] folder (which is a child of INSTALLDIR) could be easily found in the search. Otherwise, you may need to first perform a file search for your main executable, and use its location to set the INSTALLDIR, before you start looking for files in sub folders. Note that you can order the searches, so the result of one search can be used somewhere in a later search. I hope this makes sense.

        Now, with these additional explanations and the project file in hands, you should be able to replicate the behavior and integrate this into your MSI.

        Ulrich

        Comment


        • #5
          Dear Ulrich,

          Words can’t express how truly grateful I am to you for all the help you provided and the generous amount of accompanying materials. I went through it over and over again and everything seemed totally clear and making sense to me. However, after spending about 8 hours on playing with MSI I still haven’t made any progress, regrettably. I meticulously recreated everything according to your screens and tested dozens of little variations where I would change a thing or two hoping to get it working… it just never sees the file (unless I revert the condition to INIFILE = “NOT_FOUND”, then it does the job but what’s the point).
          Could I kindly ask you to review the four images and the verbose output log I’m attaching here? (for some reason I had to change the .log extension to .txt to make the uploader happy)
          I hope it would shed some light on it. The log, however, always looks the same:

          Code:
          MSI (s) (68:C0) [15:19:34:766]: Component: INISet_0001; Installed: Absent;   Request: Local;   Action: Null
          MSI (s) (68:C0) [15:19:34:766]: Component: INISet_0002; Installed: Absent;   Request: Local;   Action: Null
          MSI (s) (68:C0) [15:19:34:766]: Component: INISet_0003; Installed: Absent;   Request: Local;   Action: Null
          MSI (s) (68:C0) [15:19:34:766]: Component: INISet_0004; Installed: Absent;   Request: Local;   Action: Null
          Otherwise, I guess I’ll continue with LUA which I was half way through anyway, although it looks even less attractive to me now that I’m aware of this much simpler and more elegant approach.

          I did run the sample project file as well together with a simple test INI similar to yours. Still no luck.

          Thank you so much again for everything,
          Leon
          Attached Files

          Comment


          • #6
            Hello again. From the log file, I see that Windows Installer was unable to find the INI file at the specified path. Windows Installer was unable to find the file "C:\Program Files (x86)\FT INTERACTIVE DATA\MIPS\INI\CPS.INI", as the Property INIFILE_CPS was set with the value "NOT_FOUND".

            I suggest that you execute
            Code:
            DIR "C:\Program Files (x86)\FT INTERACTIVE DATA\MIPS\INI\CPS.INI"
            at the command prompt, and see if the file does indeed exist. It could be that there is an extra space somewhere in the actual path which isn't easily visible in Windows Explorer...

            Ulrich

            Comment


            • #7
              Unbelievable but true: the file appears to be there, as well as the other six INIs…

              Code:
              H:\>DIR "C:\Program Files (x86)\FT INTERACTIVE DATA\MIPS\INI\CPS.INI"
               Volume in drive C has no label.
               Volume Serial Number is 2865-CD48
              
               Directory of C:\Program Files (x86)\FT INTERACTIVE DATA\MIPS\INI
              
              01/28/2016  03:23 PM               649 CPS.INI
                             1 File(s)            649 bytes
                             0 Dir(s)  404,426,448,896 bytes free
              Code:
              H:\>DIR "C:\Program Files (x86)\FT INTERACTIVE DATA\MIPS\INI"
               Volume in drive C has no label.
               Volume Serial Number is 2865-CD48
              
               Directory of C:\Program Files (x86)\FT INTERACTIVE DATA\MIPS\INI
              
              02/01/2016  05:32 PM    <DIR>          .
              02/01/2016  05:32 PM    <DIR>          ..
              04/30/2014  03:54 PM               486 CORP.INI
              01/28/2016  03:23 PM               649 CPS.INI
              04/30/2014  03:55 PM               801 EQUITY.INI
              04/30/2014  03:55 PM               824 GENSUMM.INI
              04/30/2014  03:55 PM               733 ISSUER.INI
              04/30/2014  03:56 PM               318 MPS.INI
              01/13/2016  10:56 AM               709 OPS.INI
                             7 File(s)          4,520 bytes
                             2 Dir(s)  404,426,469,376 bytes free
              Really don't know what to think. I'm very sorry for tiring you up with this unfortunate problem.

              Comment


              • #8
                Okay, before I throw the towel, could you please create a Zip of your "C:\Program Files (x86)\FT INTERACTIVE DATA" folder, and post it here. Please remove all irrelevant files, but please keep the "INI" folder untouched. I would like to decompress the zip in a VM and replicate your folder structure, then run my sample MSI against it and see if it also cannot find the file. I have no explanation. Aliens? We'll see.

                Ulrich

                Comment


                • #9
                  Ulrich,
                  Done - I truly appreciate your help, thank you so much. Attached are the compressed folder structure (I removed everything irrelevant as you said), a Win Explorer screenshot of abovementioned structure and also my zipped .msifact file. There must be little something besides aliens that I'm just not doing right. My towel is waving already. The other hand is finalizing the LUA script..
                  Leon
                  Attached Files

                  Comment


                  • #10
                    I see the issue. Please give me some time to discover what is going on.

                    Ulrich

                    Comment


                    • #11
                      I don't know how to thank you properly, Ulrich. I'm starting to see the light at the end of this endless tunnel.

                      Comment


                      • #12
                        I took your original project file and made a small modification. I added a Folder Search, as shown here:

                        Click image for larger version

Name:	SCRN-2016-02-05-01.png
Views:	1
Size:	10.9 KB
ID:	284386

                        I look for the folder holding the INI files, starting at [ProgramFilesFolder]. The full path, if found, will be stored in the Property SEARCH_INI_FOLDER.

                        Then I changed the folder where the "CPS.INI" file should be searched in as the [SEARCH_INI_FOLDER]:

                        Click image for larger version

Name:	SCRN-2016-02-05-02.png
Views:	1
Size:	14.3 KB
ID:	284387

                        The rest was kept unchanged. Now, I don't know why the original code did no longer work - it did in my initial tests. It might be a bug, I am really not sure at this point. Could you please try out this edited code?

                        Ulrich

                        Comment


                        • #13
                          Finally, it worked!! I owe you so much gratitude, Ulrich, thanks a million. I’m just so puzzled why would need to add an extra search, redundant in some parts, to make it work… But that’s fine as long as it does the job. By the way, as an alternative, let me post my LUA script here that does the same. It’s very generic and can be easily modified to do any kind of a similar task. Just in case someone needs it. I wish there were [LUA] tags to wrap this code with and make it a bit more readable.


                          Code:
                          -- global error checking function
                          function CheckINIValueSet ( FileName, Key )
                          	-- Get the error code of the last action.
                          	error = Application.GetLastError();				
                          	-- If an error occurred, display an error message.
                          	if (error ~= 0) then
                          	    Dialog.Message("Error Setting " .. Key .. " in " .. FileName, _tblErrorMessages[error], MB_OK, MB_ICONEXCLAMATION);
                          	end
                          end 
                          
                          -- Get the path to the user's MIPS INI folder.
                          INIFilesFolder = Shell.GetFolder( SHF_PROGRAMFILES ) .. "\\FT Interactive Data\\Mips\\Ini";
                          
                          -- if it is not found, pop an error
                          error = Application.GetLastError();
                          if error ~= 0 then
                                  Dialog.Message("Error", "Could not get INI Folder. " .. _tblErrorMessages[error]);
                                  
                          -- process the INIs if folder is found
                          else
                          
                          	INIFilesTable = {"CORP.INI", "CPS.INI", "EQUITY.INI", "GENSUMM.INI", "ISSUER.INI", "MPS.INI", "OPS.INI" };
                          	message = "Processed following INI Files in the " .. INIFilesFolder .. ":\r\n\r\n";
                          	
                          	for n, MyINIFile in INIFilesTable do
                          	
                          		search_results = File.Find( INIFilesFolder, MyINIFile, true, false, nil );
                          		if ( search_results ~= nil ) then
                          		
                          			-- get full path to the INI file
                          			filepath = INIFilesFolder .. "\\" .. MyINIFile;
                          			
                          			-- get all Keys in [HOSTS] section
                          			all_keys = INIFile.GetValueNames( filepath, "HOSTS" );
                          			
                          			if ( all_keys ~= nil ) then
                          				-- loop through keys and set values to "mmvprod"
                          				for index_key, key in all_keys do
                          				
                          					INIFile.SetValue( filepath, "HOSTS", key, "mmvprod" );
                                      		                CheckINIValueSet ( filepath, key );	
                                      					
                          				end
                          			end
                          			
                          			message = String.Concat( message, MyINIFile .. "\r\n" );			
                          		end 
                          	end
                          
                          	Dialog.Message( "INI", message );
                          
                          --Debug.ShowWindow(true);
                          
                          end
                          
                          -- If an error occurred, display the error message.
                          if ( error ~= 0 ) then
                              Dialog.Message( "Error", _tblErrorMessages[error], MB_OK, MB_ICONEXCLAMATION );
                          end

                          Comment

                          Working...
                          X