locked
[C#] Path & arguments strings

    Question

  • Hi everybody,

    Looking for uninstall strings in Registry (Path=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, Key=UninstallString), I've found some 'bad' pathes on Adobe Products (reported as Bug 3172314 : [url]https://bugbase.adobe.com/index.cfm?event=bug&id=3172314[/url]).

    I think this is not the only product affected.

    Here are some VALID pathes found (for me) :
    ---------------------------------------------------------
    MsiExec.exe /I{8C6D6116-B724-4810-8F2D-D047E6B7D68E}
    "C:\Program Files\Common Files\Microsoft Shared\OFFICE14\Oarpmany.exe" /removereleaseinpatch "{90140000-001F-0401-0000-0000000FF1CE}"  "1036" "0"
    "C:\Program Files\7-Zip\Uninstall.exe"
    "C:\Program Files\Easeware\DriverEasy\unins000.exe" /SILENT

    Here are some INVALID pathes found (for me) :
    ------------------------------------------------------------
    c:\Program Files\Common Files\Adobe AIR\Versions\1.0\Resources\Adobe AIR Updater.exe -arp:uninstall

    In this last case, string parser will find :
    Arg0 = c:\Program
    Arg1 = Files\Common[...]

    The only way I found is to parse and combine each argument while is exist on drive but it looks like too piggy code.

    So, what's the best way to get
    Arg0 = c:\Program Files\Common Files\Adobe AIR\Versions\1.0\Resources\Adobe AIR Updater.exe
    Arg1 = -arp:uninstall

    Thanks,

    Vincent

    mardi 24 avril 2012 11:28

Réponses

  • Hi Vincent,

    Welcome to the MSDN Forum.

    Based on your reference, it seems that the Adobe doesn't write the double quotes signal into the register. As an end user - you, in my opinion, cannot change the behavior of Adobe program, so I think you can splice the arguments until you got an arguments contains ".exe"

    For example: 

    string path = "";
    foreach (string s in args) {
    	path += s;
    	if (s.ToLower().Contains(".exe") == true) {
    		break; // TODO: might not be correct. Was : Exit For
    	}
    }

    you can get the correct path by this way.

    I hope this will be helpful.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    mercredi 25 avril 2012 11:33
  • Hi. I've found the solution using Mike point of view.
    In the sample below, we're scanning Registry to get program uninstall string. It seems to work perfectly on my computer :)

    /// <summary>
    /// Get full filename and its arguments from a string
    /// </summary>
    /// <author>Vincent Duvernet</author>
    /// <company>Nolmë Informatique</company>
    /// <web>http://www.nolme.com</web>
    /// 
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Win32;
    using System.Text.RegularExpressions;
    using System.IO;
    using System.Globalization;
    
    /// <summary>
    /// Main namespace
    /// </summary>
    namespace ConsoleApplication1
    {
        class Program
        {
            /// <summary>
            /// Get file presence
            /// </summary>
            /// <param name="pathToCheck">Path to check (can contain %VARIABLE%, filename without extension, no path specified, no ~</param>
            /// <returns>Return the full expanded filename</returns>
            static string IsFileExist(string pathToCheck)
            {
                string strResult = null;
                
                // First check : no string manipulation
                string strPathToCheckTrimmed = pathToCheck.Trim();
                if (File.Exists(strPathToCheckTrimmed))
                {
                    strResult = strPathToCheckTrimmed;
                }
                else
                {
                    // Second check : expand variables, remove quotes & spaces
                    string strExpandedPath = Environment.ExpandEnvironmentVariables(strPathToCheckTrimmed);
                    string strExpandedPathTrimmed = strExpandedPath.Replace('"', ' ').Trim();
                    if (File.Exists(strExpandedPathTrimmed))
                    {
                        strResult = strExpandedPathTrimmed;
                    }
                    else
                    {
                        // Third check : use environment variable
                        string strAllPathes = Environment.GetEnvironmentVariable("PATH");
                        string strAllPathesExt = Environment.GetEnvironmentVariable("PATHEXT");
                        string pattern = ";(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))";
                        Regex r = new Regex(pattern);
                        string[] splittedPathArray = r.Split(strAllPathes);
                        string[] splittedPathExtArray = r.Split(strAllPathesExt);
    
                        // Loop on all paths
                        for (int i = 0; i < splittedPathArray.Length; i++)
                        {
                            string strTempPath = Path.Combine(splittedPathArray[i], pathToCheck);
                            if (File.Exists(strTempPath))
                            {
                                // Ok it works ^^, we can stop searching other file instance elsewhere
                                strResult = strTempPath;
                                break;
                            }
                            else
                            {
                                // Fourth check : scan will all file extensions
                                for (int j = 0; j < splittedPathExtArray.Length; j++)
                                {
                                    if (File.Exists(strTempPath + splittedPathExtArray[j]))
                                    {
                                        // Ok it works ^^, we can stop searching other file instance elsewhere
                                        strResult = strTempPath + splittedPathExtArray[j];
                                        j = splittedPathExtArray.Length;
                                        i = splittedPathArray.Length;
                                    }
                                }
                            }
                        }
                    }
                }
    
                // Expand path to remove ~ inside short filename
                if (strResult != null)
                {
                    strResult = Path.GetFullPath(strResult);
                }
    
                return strResult;
            }
    
            /// <summary>
            /// Split a buffer into full filename + arguments
            /// </summary>
            /// <param name="bufferToManage">buffer to manage</param>
            /// <returns>Full filename as arg0 and arguments after. Can be NULL if file don't exist</returns>
            /// <remarks>Thanks for the article on TechRepublic for RegEx</remarks>
            /// <remarks>Take care if destination file DON'T EXIST</remarks>
            /// <see cref="http://www.techrepublic.com/article/easily-parse-string-values-with-net/6030362"/>
            static string[] CheckPath(string bufferToManage)
            {
                string strTrimBuffer = bufferToManage.Trim();
                string[] Result = null;
    
                string pattern = " (?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))";
                Regex r = new Regex(pattern);
                string[] splittedArray = r.Split(strTrimBuffer);
                
                MatchCollection matches = r.Matches(strTrimBuffer);
    
                // First part : during split, spaces have been removed, so, add it again
                int iPosition = 0;
                for (int i = 0; i < splittedArray.Length; i++)
                {
                    string strCurrentArgument = splittedArray[i];
    
                    if (!strCurrentArgument.EndsWith(@""""))
                    {
                        if ((iPosition + strCurrentArgument.Length + 1 < strTrimBuffer.Length) && (strTrimBuffer[iPosition + strCurrentArgument.Length] == ' '))
                        {
                            // Add spaces removed during Regex split
                            splittedArray[i] = strCurrentArgument + " ";
                        }
                    }
                    iPosition += splittedArray[i].Length;
                }
    
                // Loop inside arguments to get the application application full path
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < splittedArray.Length; j++)
                {
                    sb.Append(splittedArray [j]);
    
                    // Expand path, remove double quotes, remove exterior spaces
                    string strExpandedPath = IsFileExist(sb.ToString());
                    if (strExpandedPath != null)
                    {
                        // Count empty elements (in case of multiple spaces between each argument)
                        int iInvalidArguments = 0;
                        for (int k = 0; k < splittedArray.Length; k++)
                        {
                            splittedArray[k] = splittedArray[k].Trim();
                            if (String.IsNullOrEmpty (splittedArray[k]))
                            {
                                iInvalidArguments++;
                            }
                        }
    
                        // File exist, all items remaining are arguments
                        int iNumberOfArguments = splittedArray.Length - (j + 1) - iInvalidArguments;
    
                        // Create destination buffer
                        Result = new string[iNumberOfArguments + 1];
                        
                        // Save program full path
                        Result[0] = strExpandedPath;
    
                        // Save program parameters
                        int iCurrentArgumentOffset = 1;
                        for (int k = 0; k < iNumberOfArguments + iInvalidArguments; k++)
                        {
                            if (!String.IsNullOrEmpty(splittedArray[j + k + 1]))
                            {
                                Result[iCurrentArgumentOffset] = splittedArray[j + k + 1];
                                iCurrentArgumentOffset++;
                            }
                        }
                        break;
                    }
                }
    
                return Result;
            }
    
            /// <summary>
            /// Main program entry
            /// </summary>
            /// <param name="args"></param>
            static void Main(string[] args)
            {
                // Loop on Registry
                RegistryKey RootKey = Registry.LocalMachine;
                string strPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\";
                string[] Result;
    
                RegistryKey CurrentKey = RootKey.OpenSubKey(strPath, false);
                if (CurrentKey != null)
                {
                    string[] strGuidArray = CurrentKey.GetSubKeyNames();
                    for (int i = 0; i < strGuidArray.Length; i++)
                    {
                        RegistryKey CurrentProgramKey = CurrentKey.OpenSubKey(strGuidArray[i], false);
    
                        if (CurrentProgramKey != null)
                        {
                            object  RegistryValue = CurrentProgramKey.GetValue("UninstallString");
                            if (RegistryValue != null)  // UninstallString is not always defined
                            {
                                string strUninstallString = RegistryValue.ToString();
    
                                if (!String.IsNullOrEmpty(strUninstallString))
                                {
                                    Result = CheckPath(strUninstallString);
                                    if (Result != null)
                                    {
                                        Console.WriteLine("Result n° {0} : {1}", i, strGuidArray[i]);
                                        Console.WriteLine("----------------");
                                        for (int j = 0; j < Result.Length; j++)
                                        {
                                            Console.WriteLine("Arg. {0}: <{1}>", j, Result[j]);
                                        }
                                        Console.WriteLine("");
                                    }
                                }
                            }
                        }
                    }
                }
    
                Console.WriteLine("Finished");
                Console.ReadKey();
            }
        }
    }
    

    mercredi 25 avril 2012 21:57

Toutes les réponses

  • what are the classes and methods you are using, that leads to this behaviour ?
    mardi 24 avril 2012 13:37
  • Hi Vincent,

    Welcome to the MSDN Forum.

    Based on your reference, it seems that the Adobe doesn't write the double quotes signal into the register. As an end user - you, in my opinion, cannot change the behavior of Adobe program, so I think you can splice the arguments until you got an arguments contains ".exe"

    For example: 

    string path = "";
    foreach (string s in args) {
    	path += s;
    	if (s.ToLower().Contains(".exe") == true) {
    		break; // TODO: might not be correct. Was : Exit For
    	}
    }

    you can get the correct path by this way.

    I hope this will be helpful.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    mercredi 25 avril 2012 11:33
  • @ Sezhiyan Thiagarajan :

    I'm using standard registry methods :

     RegistryKey RootKey = Registry.LocalMachine;
    string strPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + options.Guid;
    RegistryKey CurrentKey = RootKey.OpenSubKey(strPath, false);
    if (CurrentKey != null)
    {
       string strUninstallString = CurrentKey.GetValue("UninstallString").ToString ();

       [...]
       Process.Start ();
    }

    @ Mike Feng :

    I think you're on the way but we can't be sure file is .EXE instead of .CMD, .BAT, .COM...

    And the executable may not contain file extension. So tomorrow, this string will (still) be correct for Adobe product ?

    "c:\Program Files\Common Files\Adobe AIR\Versions\1.0\Resources\Adobe AIR Updater"

    mercredi 25 avril 2012 14:15
  • For generic case (so we exclude Adobe vision of path... :/), we can have the expecting result using Regex :

    static string[] CheckPath(string bufferToManage)
            {
                string strTrimBuffer = bufferToManage.Trim();
                string[] Result = null;

               string pattern = " (?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))";
                Regex r = new Regex(pattern);
                string[] sites = r.Split(strTrimBuffer);
                foreach (string s in sites)
                {
                    Console.WriteLine("Arg: <" + s +">");
                }

                return Result;
            }

    mercredi 25 avril 2012 17:46
  • Hi. I've found the solution using Mike point of view.
    In the sample below, we're scanning Registry to get program uninstall string. It seems to work perfectly on my computer :)

    /// <summary>
    /// Get full filename and its arguments from a string
    /// </summary>
    /// <author>Vincent Duvernet</author>
    /// <company>Nolmë Informatique</company>
    /// <web>http://www.nolme.com</web>
    /// 
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Win32;
    using System.Text.RegularExpressions;
    using System.IO;
    using System.Globalization;
    
    /// <summary>
    /// Main namespace
    /// </summary>
    namespace ConsoleApplication1
    {
        class Program
        {
            /// <summary>
            /// Get file presence
            /// </summary>
            /// <param name="pathToCheck">Path to check (can contain %VARIABLE%, filename without extension, no path specified, no ~</param>
            /// <returns>Return the full expanded filename</returns>
            static string IsFileExist(string pathToCheck)
            {
                string strResult = null;
                
                // First check : no string manipulation
                string strPathToCheckTrimmed = pathToCheck.Trim();
                if (File.Exists(strPathToCheckTrimmed))
                {
                    strResult = strPathToCheckTrimmed;
                }
                else
                {
                    // Second check : expand variables, remove quotes & spaces
                    string strExpandedPath = Environment.ExpandEnvironmentVariables(strPathToCheckTrimmed);
                    string strExpandedPathTrimmed = strExpandedPath.Replace('"', ' ').Trim();
                    if (File.Exists(strExpandedPathTrimmed))
                    {
                        strResult = strExpandedPathTrimmed;
                    }
                    else
                    {
                        // Third check : use environment variable
                        string strAllPathes = Environment.GetEnvironmentVariable("PATH");
                        string strAllPathesExt = Environment.GetEnvironmentVariable("PATHEXT");
                        string pattern = ";(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))";
                        Regex r = new Regex(pattern);
                        string[] splittedPathArray = r.Split(strAllPathes);
                        string[] splittedPathExtArray = r.Split(strAllPathesExt);
    
                        // Loop on all paths
                        for (int i = 0; i < splittedPathArray.Length; i++)
                        {
                            string strTempPath = Path.Combine(splittedPathArray[i], pathToCheck);
                            if (File.Exists(strTempPath))
                            {
                                // Ok it works ^^, we can stop searching other file instance elsewhere
                                strResult = strTempPath;
                                break;
                            }
                            else
                            {
                                // Fourth check : scan will all file extensions
                                for (int j = 0; j < splittedPathExtArray.Length; j++)
                                {
                                    if (File.Exists(strTempPath + splittedPathExtArray[j]))
                                    {
                                        // Ok it works ^^, we can stop searching other file instance elsewhere
                                        strResult = strTempPath + splittedPathExtArray[j];
                                        j = splittedPathExtArray.Length;
                                        i = splittedPathArray.Length;
                                    }
                                }
                            }
                        }
                    }
                }
    
                // Expand path to remove ~ inside short filename
                if (strResult != null)
                {
                    strResult = Path.GetFullPath(strResult);
                }
    
                return strResult;
            }
    
            /// <summary>
            /// Split a buffer into full filename + arguments
            /// </summary>
            /// <param name="bufferToManage">buffer to manage</param>
            /// <returns>Full filename as arg0 and arguments after. Can be NULL if file don't exist</returns>
            /// <remarks>Thanks for the article on TechRepublic for RegEx</remarks>
            /// <remarks>Take care if destination file DON'T EXIST</remarks>
            /// <see cref="http://www.techrepublic.com/article/easily-parse-string-values-with-net/6030362"/>
            static string[] CheckPath(string bufferToManage)
            {
                string strTrimBuffer = bufferToManage.Trim();
                string[] Result = null;
    
                string pattern = " (?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))";
                Regex r = new Regex(pattern);
                string[] splittedArray = r.Split(strTrimBuffer);
                
                MatchCollection matches = r.Matches(strTrimBuffer);
    
                // First part : during split, spaces have been removed, so, add it again
                int iPosition = 0;
                for (int i = 0; i < splittedArray.Length; i++)
                {
                    string strCurrentArgument = splittedArray[i];
    
                    if (!strCurrentArgument.EndsWith(@""""))
                    {
                        if ((iPosition + strCurrentArgument.Length + 1 < strTrimBuffer.Length) && (strTrimBuffer[iPosition + strCurrentArgument.Length] == ' '))
                        {
                            // Add spaces removed during Regex split
                            splittedArray[i] = strCurrentArgument + " ";
                        }
                    }
                    iPosition += splittedArray[i].Length;
                }
    
                // Loop inside arguments to get the application application full path
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < splittedArray.Length; j++)
                {
                    sb.Append(splittedArray [j]);
    
                    // Expand path, remove double quotes, remove exterior spaces
                    string strExpandedPath = IsFileExist(sb.ToString());
                    if (strExpandedPath != null)
                    {
                        // Count empty elements (in case of multiple spaces between each argument)
                        int iInvalidArguments = 0;
                        for (int k = 0; k < splittedArray.Length; k++)
                        {
                            splittedArray[k] = splittedArray[k].Trim();
                            if (String.IsNullOrEmpty (splittedArray[k]))
                            {
                                iInvalidArguments++;
                            }
                        }
    
                        // File exist, all items remaining are arguments
                        int iNumberOfArguments = splittedArray.Length - (j + 1) - iInvalidArguments;
    
                        // Create destination buffer
                        Result = new string[iNumberOfArguments + 1];
                        
                        // Save program full path
                        Result[0] = strExpandedPath;
    
                        // Save program parameters
                        int iCurrentArgumentOffset = 1;
                        for (int k = 0; k < iNumberOfArguments + iInvalidArguments; k++)
                        {
                            if (!String.IsNullOrEmpty(splittedArray[j + k + 1]))
                            {
                                Result[iCurrentArgumentOffset] = splittedArray[j + k + 1];
                                iCurrentArgumentOffset++;
                            }
                        }
                        break;
                    }
                }
    
                return Result;
            }
    
            /// <summary>
            /// Main program entry
            /// </summary>
            /// <param name="args"></param>
            static void Main(string[] args)
            {
                // Loop on Registry
                RegistryKey RootKey = Registry.LocalMachine;
                string strPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\";
                string[] Result;
    
                RegistryKey CurrentKey = RootKey.OpenSubKey(strPath, false);
                if (CurrentKey != null)
                {
                    string[] strGuidArray = CurrentKey.GetSubKeyNames();
                    for (int i = 0; i < strGuidArray.Length; i++)
                    {
                        RegistryKey CurrentProgramKey = CurrentKey.OpenSubKey(strGuidArray[i], false);
    
                        if (CurrentProgramKey != null)
                        {
                            object  RegistryValue = CurrentProgramKey.GetValue("UninstallString");
                            if (RegistryValue != null)  // UninstallString is not always defined
                            {
                                string strUninstallString = RegistryValue.ToString();
    
                                if (!String.IsNullOrEmpty(strUninstallString))
                                {
                                    Result = CheckPath(strUninstallString);
                                    if (Result != null)
                                    {
                                        Console.WriteLine("Result n° {0} : {1}", i, strGuidArray[i]);
                                        Console.WriteLine("----------------");
                                        for (int j = 0; j < Result.Length; j++)
                                        {
                                            Console.WriteLine("Arg. {0}: <{1}>", j, Result[j]);
                                        }
                                        Console.WriteLine("");
                                    }
                                }
                            }
                        }
                    }
                }
    
                Console.WriteLine("Finished");
                Console.ReadKey();
            }
        }
    }
    

    mercredi 25 avril 2012 21:57
  • Hi Duvernet,

    Congratulations. 

    Thank you for sharing the whole solution here. It will be useful for the other community members.

    Best regards.


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    jeudi 26 avril 2012 03:06