Aracılığıyla paylaş


CustomActionData and User Defined Path

When designing a setup project, you can define text boxes dialog to input a file system path. To process the input that the user has entered, usually a custom action is created to handle the user input, and the user input is passed through CustomActionData property. Unfortunately, there are several problems when dealing with path.

  • Spaces in the path
    A space is a valid character for a path. If the user passes a path with a space in it, you should enclose the input with double-quotes, for example, this is a CustomActionData property value: /USERDEFINEDDATA="[USERPATH]".
    This leads to a different problem below.

  • Backslashes trimmed.
    When the user enters a UNC path starting with "\\", if the value property is enclosed with double-quotes, the string that is passed to the custom action contains only one '\'. Not really a problem, you have to check if the path is a UNC path and make sure there are two '\'.

  • Trailing backslash caused an exception.
    This is an interesting problem. As soon as the user enters a path, either absolute path or a UNC path and ended the path with a backslash, the user will get this error when running the setup. 
    Error 1001: Exception occurred while initializing the installation:
    System.IO.FileNotFoundException. Could not load file or assembly:
    'file:///C:\WINNT\[Your assembly name here]' or one of its dependencies.
    The system could not find the file specified.

    This error happens only when the CustomActionData inside double-quotes.

    If you check my previous post about passing [TARGETDIR] to custom action, you notice that you have to pass a backslash at the end. You can do the same with a user defined path, something like this /USERDEFINEDDATA="[USERPATH]\". This code works great if the user enters a path with a trailing backslash, and there will be only ONE trailing backslash. The problem is if the user did not supply the trailing backslash, the user will get the same 1001 error again.

    My solution is this, set CustomActionData with a character that you know is not a valid path character, for example a Pipe character ('|'). Assign your CustomActionData like this /USERDEFINEDDATA="[USERPATH] | ". With this, a user defined path with trailing backslash will come to the custom action as "C:\some Path\some path\|", and user defined path without a trailing backslash will come to the custom action as "C:\Some Path\Some Path|". What you have to do now is to trim the '|' character, and you are good. Now you have a code that can handle user input with or without trailing backslash.

Comments

  • Anonymous
    October 29, 2008
    Couldn't you do something like /USERDEFINEDDATA=@"[USERPATH]"?

  • Anonymous
    October 30, 2008
    Andrew, Unfortunately, that won't work. @ works with C#, but not with MSI. MSI does not escape the string if you use @. If you inspect the MSI using Orca, you will see that the @ sign will be there, just like you wrote it. When the value is passed to your code, it will include the @ sign. Let says you passed c:Windows, your custom action will see "@c:Windows", and trailing backslash will still cause a problem.

  • Anonymous
    December 15, 2009
    Thanks! You have seriously saved my day with this tiny (but very important!) information. I can't count the number of sites that I have visited that never mentioned the problem with the tailing backslash combined with the quotes.

  • Anonymous
    July 20, 2010
    can we give conditional value e.g. /USERDEFINEDDATA="[USERPATH]" OR "[TARGETPATH]" WHICHEVER BETWEEN THE TWO IS NOT BLANK OR NULL

  • Anonymous
    November 09, 2010
    Thanks! man you have solved my hardest problem. GREAT!

  • Anonymous
    April 30, 2012
    When a single double quotation (“) is passed as an argument to a custom action, the installer will fail with the same error message. Is there any workaround available for it?

  • Anonymous
    June 19, 2012
    thanks a lot ............really good. This worked /targetvdir="[TARGETVDIR]" /targetdir="[TARGETDIR]"