Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fileshortcuts starting with an integer #307

Closed
naloney opened this issue Mar 3, 2018 · 14 comments
Closed

Fileshortcuts starting with an integer #307

naloney opened this issue Mar 3, 2018 · 14 comments

Comments

@naloney
Copy link

naloney commented Mar 3, 2018

Hi Oleg,

I've come across a bit of strange behaviour, if you take the following code sample:

                var project =
                    new Project("My Product",
                        new Dir(new Id("INSTALL_DIR"), @"%ProgramFiles%\My Product",
                            new WixSharp.File(@"C:\Temp\Test.exe",
                                new FileShortcut("MyApp", @"%ProgramMenu%\My Product")
                                {
                                    WorkingDirectory = "[INSTALL_DIR]"
                                })));

This works fine.

However, if the shortcut location starts with an integer e.g. 7-Zip the build will fail with the error:
error LGHT0094 : Unresolved reference to symbol 'Directory:ProgramMenuFolder.1My_Product' in section 'Product:{6FE30B47-2577-43AD-9095-1861CA25889C}'

I believe you should be able to reproduce this with the following code:

                var project =
                    new Project("My Product",
                        new Dir(new Id("INSTALL_DIR"), @"%ProgramFiles%\My Product",
                            new WixSharp.File(@"C:\Temp\Test.exe",
                                new FileShortcut("MyApp", @"%ProgramMenu%\1My Product")
                                {
                                    WorkingDirectory = "[INSTALL_DIR]"
                                })));

Where the only change is the number 1 at the start of the shortcut location. Is this expected? Is there a workaround?

Thanks and keep up the great work!!

@oleg-shilo
Copy link
Owner

oleg-shilo commented Mar 4, 2018

That second parameter in the FileShortcut constructor is the id of the Dir parent where the shortcut should be created. Thus it can be new FileShortcut("MyApp", "INSTALL_DIR") or new FileShortcut("MyApp", "ProgramFilesFolder"). . .

In the code sample you based your code on the id was dynamically looked up from the dir path "@"%ProgramMenu%\My Product" but when you introduced that prefix "1" it became no longer possible to locate the directory with that path as it simply does not exist.

If the "1My Product" directory is what you want to install then you need to add it to the project so it can be resolved.

The bottom line is "the 'location' parameter is the ID of the dir where the shortcut should be created, or the path of that dir, which can be used to lookup that Id".

oleg-shilo added a commit that referenced this issue Mar 4, 2018
…arameter. It automatically creates the shortcut in the parent `Dir`. Triggered by #307.
@naloney
Copy link
Author

naloney commented Mar 4, 2018

Thanks Oleg, I can create the "%ProgramMenu%\1My Product" tree manually and assign a custom Id to the directory so that I can reference it in the shortcut. It looks like the Id autogenerator prepends an extra underscore to the Id when the directory starts with an integer?

You've probably seen the generated .wxs from both samples and Wixsharp generates the ProgramMenu directory tree in both cases but in the failing example there is an extra underscore.

           <File Id="Test.exe_597858937" Source="..\..\..\..\..\Temp\Test.exe">
              <Shortcut Id="Shortcut.Test.exe_597858937.MyApp" WorkingDirectory="INSTALL_DIR" Directory="ProgramMenuFolder.1My_Product" Name="MyApp.lnk" />
            </File>
      <Directory Id="ProgramMenuFolder" Name="ProgramMenuFolder">
        <Directory Id="ProgramMenuFolder._1My_Product" Name="1My Product">

          <Component Id="_1My_Product.EmptyDirectory" Guid="6fe30b47-2577-43ad-9095-1861db3a6053">
            <CreateFolder />
            <RemoveFolder Id="ProgramMenuFolder._1My_Product" On="uninstall" />

            <RegistryKey Root="HKCU" Key="Software\WixSharp\Used">
              <RegistryValue Value="0" Type="string" KeyPath="yes" />
            </RegistryKey>
          </Component>

        </Directory>
      </Directory>

@oleg-shilo
Copy link
Owner

...tree manually and assign a custom Id to the directory...

No, I don't think you are assigning custom ID. Otherwise "Id autogenerator" wouldn't be engaged.
Can you provide the C# code so i can see how you are assigning the dir Id?

@naloney
Copy link
Author

naloney commented Mar 4, 2018

Hi Oleg,

Apologies, I said I can assign custom Ids but I haven't yet, I'm just working on that sample code.

I'm aware that an id can't start with an integer, but in the wxs above you can see that an underscore is being placed before the '1' but as the Id is 'ProgramMenuFolder._1My_Product' then it seems that 'ProgramMenuFolder.1My_Product' should be fine and this is the directory that the shortcut is looking for?

@oleg-shilo
Copy link
Owner

Yes the underscore is inserted in front of the name/id to overcome the WiX/MSI limitations during id auto-generation. The auto-generation algorithm internals in this case are irrelevant. The problem is that you want to have/know deterministic dir Id. Thus auto generation is not a good choice by definition. you are much better of by assigning the Id and then reusing it in the FileShortcut constructor:

...
new WixSharp.File(@"C:\Temp\Test.exe",
    new FileShortcut("MyApp", "My_Product")
...
new Dir(new Id("My_Product"), @"%ProgramFiles%\1My Product",

It is still hard for me to comment as I haven't seen your C# setup definition and I have to assume that you add to you setup both "My Product" and "My Product" dirs.

Though if it is only "1My Product" then you can still use auto-generated id even without knowing it:

var project =
    new Project("My Product",
        new Dir(@"%ProgramFiles%\1My Product",
            new WixSharp.File(@"C:\Temp\Test.exe",
                new FileShortcut("MyApp", "")

By passing empty string in the "FileShortcut" constructor, you instruct WixSharp to use whatever auto-generated id of the parent Dir.

@naloney
Copy link
Author

naloney commented Mar 4, 2018

Hi Oleg,

Sure, I understand why the underscore is getting added but it's not getting added to the front of the Id, it's getting added halfway through? e.g. ProgramMenuFolder._1My_Product

My initial C# setup definition is in my first post.

Regarding your snippets above, the second one works fine, if you want the shortcut in the same folder as the actual executable, which seems unlikely.

For the first snippet, this sample builds but the shortcut ends up under %ProgramMenu% rather than %ProgramMenu%\1My Product

            var testdir = new Dir(new Id("TestId"), @"%ProgramMenu%\1My Product");

            var project =
                new Project("My Product",
                    new Dir(new Id("INSTALL_DIR"), @"%ProgramFiles%\1My Product",
                        new WixSharp.File(@"C:\Temp\Test.exe",
                            new FileShortcut("MyApp", testdir.Id))));

            project.GUID = new Guid("6fe30b47-2577-43ad-9095-1861ba25889b");
            project.UI = WUI.WixUI_ProgressOnly;
            project.OutFileName = "setup";
            project.PreserveTempFiles = true;

Sorry for all of the questions, I find WixSharp great but I'm pretty new to it!!

@oleg-shilo
Copy link
Owner

OK, There are too many confusing points here. let's sort them out one by one:

  1. ...it's getting added halfway through? e.g. ProgramMenuFolder._1My_Product...
    Correct, it is not exactly the way it was anticipated but since it does not trigger even a single problem I treat it as an auto-generated code aesthetics issue. More importantly, it has nothing to do with the problem you are experiencing.

  2. ...My initial C# setup definition is in my first post....
    Correct, the first snippet was provided. But I wanted to see your final code where you incorporate my recommendation about adding "1My Product" to the project. You provided it now. All good.

  3. ...For the first snippet, this sample builds but the shortcut ends up under %ProgramMenu% rather than %ProgramMenu%\1My Product...
    I am not sure I understand the intention of your code in your post above.
    You create folder "%ProgramFiles%\1My Product" containing Test.exe. You also create the shortcut to Test.exe. However you indicate that you want to place the shortcut file during setup in the directory with the id testdir.Id. But.... this directory is not a part of your setup. It's just hanging in the air. You declared it in the first code line but never added to the setup.... Even more puzzling is the face that both dirs (testdir.Id and INSTALL_DIR) are the definitions of the same target folder "%ProgramMenu%\1My Product".
    Is it typo?
    If you only wanted to create '1My Product' folder and place the shortcut into it (most likely) then it is what my sample does:

    var project =
        new Project("My Product",
            new Dir(@"%ProgramFiles%\1My Product",
                new WixSharp.File(@"C:\Temp\Test.exe",
                    new FileShortcut("MyApp", "")

@naloney
Copy link
Author

naloney commented Mar 5, 2018

Hi Oleg,

For clarity, here is what I am trying to achieve: I want to be able to add a single file to a directory and have a shortcut for the file in the start menu under a subfolder that starts with a number.

To your points:

  1. I don't see this as cosmetic otherwise the wxs in my very first sample would compile. There seem to be two ways of the directoy Id being generated, so in the wxs above you can see that in the directory tree the Id contains the extra underscore whereas in the shortcut location you can see that it is expecting an Id without that extra underscore.

  2. Ok.. sure but your suggestions are workarounds to the "cosmetic" autogenerated Id issue

  3. For the first point I did miss out a line of the code which should have been like this

....
                new Project("My Product",
                    testdir,
                    new Dir(new Id("INSTALL_DIR"), @"%ProgramFiles%\1My Product"
....

However as I said above when you build this sample the shortcut ends up under %ProgramMenu% rather than %ProgramMenu%\1My Product

Also please re-read my code and you will see that the exe goes under %ProgramFiles% whilst the shortcut goes under %ProgramMenu% although there was a line missing, hopefully you could see the intention by the use of testdir.Id

For your final sample, as I have already said, if I wanted to create a shortcut in exactly the same folder as the exe it points to then your sample works fine, this is not what I am trying to do.

Please if this is only a cosmetic issue, could you post a sample which achieves what I stated as the aim in the first sentence in this post?

@oleg-shilo
Copy link
Owner

Now we are getting something. :)

The crucial piece of information is that "%ProgramFiles% whilst the shortcut goes under %ProgramMenu%". Everything else is almost irrelevant.

The solution to your problem is illustrated by the Shortcuts-2 sample:

var project =
        new Project("My Product",
            new Dir(@"%ProgramFiles%\My Company\My Product",
                new File(@"AppFiles\MyApp.exe",
                    new FileShortcut("MyApp", "") { WorkingDirectory = "[INSTALL_DIR]" }),
                new ExeFileShortcut("Uninstall MyApp", "[System64Folder]msiexec.exe", "/x [ProductCode]")),

            new Dir(@"%ProgramMenu%\My Company\1My Product",
                new ExeFileShortcut("Uninstall MyApp", "[System64Folder]msiexec.exe", "/x [ProductCode]"),
                new ExeFileShortcut("MyApp", "[INSTALL_DIR]MyApp.exe", "")),
        ...

The sample work OK with the folder name starting with a number regardless of what ID allocation technique you are using:

new Dir(@"%ProgramMenu%\My Company\1My Product",
    new ExeFileShortcut("Uninstall MyApp", "[System64Folder]msiexec.exe", "/x [ProductCode]"),
    new ExeFileShortcut("MyApp", "[INSTALL_DIR]MyApp.exe", "")),
// or
new Dir(new Id("my_id") @"%ProgramMenu%\My Company\1My Product",
    new ExeFileShortcut("Uninstall MyApp", "[System64Folder]msiexec.exe", "/x [ProductCode]"),
    new ExeFileShortcut("MyApp", "[INSTALL_DIR]MyApp.exe", "")),

The original problem was caused by the failure to provide the id of the ProgramMenu dir, which was not part of the setup. In MSI/WiX you cannot create the shortcut in the directory unless you make this directory a part of your setup. This is what I meant about your ProgramMenu dir "hanging in the air". And when you add ProgramMenu then you can reference it by it's id either custom or auto-generated. Though for your task you don't even need to.


Now, about that "cosmetic auto-generated Id issue". I am extremely keen to identify all cases where it prevents compiling an MSI file. In this case it didn't. Some other bits of code did but that leading underscore was not a guilty part. That's why I qualify it as an "auto-generated code aesthetics issue".

However if you identified (during this investigation) a case where it indeed fails then please log a dedicated issue and we will proceed from there.

@naloney
Copy link
Author

naloney commented Mar 8, 2018

Hi Oleg,

Sorry for the delay, I haven't had chance to look at this until today...

I'd prefer to not use the ExeFileShortcut class because, as far as I know, it doesn't support advertised shortcuts?

I tried the following sample code and although the shortcuts/directory are created successfully an extra empty directory called "My_Product" under the root of C:\ (TARGETDIR), how can I avoid this extra folder being created?

Sample:

            var project =
                new Project("My Product",
                    new Dir(new Id("My_Product"), @"%ProgramMenu%\1My Product"),
                    new Dir(new Id("INSTALL_DIR"), @"%ProgramFiles%\1My Product",
                        new WixSharp.File(@"C:\Temp\Test.exe",
                            new FileShortcut("My App", "My_Product") { Advertise = true })));

Generated XML:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="6fe30b47-2577-43ad-9095-1861ca25889c" Name="My Product" Language="1033" Codepage="Windows-1252" Version="1.0.0.0" UpgradeCode="6fe30b47-2577-43ad-9095-1861ba25889b" Manufacturer="admin">
    <Package InstallerVersion="200" Compressed="yes" SummaryCodepage="Windows-1252" Languages="1033" />
    <Media Id="1" Cabinet="My_Product.cab" EmbedCab="yes" />

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramMenuFolder" Name="ProgramMenuFolder">
        <Directory Id="My_Product" Name="1My Product">

          <Component Id="My_Product.EmptyDirectory" Guid="6fe30b47-2577-43ad-9095-18611cb1741a">
            <CreateFolder />
            <RemoveFolder Id="My_Product" On="uninstall" />

            <RegistryKey Root="HKCU" Key="Software\WixSharp\Used">
              <RegistryValue Value="0" Type="string" KeyPath="yes" />
            </RegistryKey>
          </Component>

        </Directory>
      </Directory>

      <Directory Id="ProgramFilesFolder" Name="ProgramFilesFolder">
        <Directory Id="INSTALL_DIR" Name="1My Product">

          <Component Id="Component.Test.exe" Guid="6fe30b47-2577-43ad-9095-18610ddf1bc9">
            <File Id="Test.exe" Source="..\..\..\..\..\Temp\Test.exe" KeyPath="yes">
              <Shortcut Id="Shortcut.Test.exe.My_App" WorkingDirectory="INSTALL_DIR" Directory="My_Product" Name="My App.lnk" Advertise="yes" />
            </File>
          </Component>

        </Directory>
      </Directory>

      <Directory Id="TARGETDIR.My_Product" Name="My_Product">

        <Component Id="Dir..EmptyDirectory" Guid="6fe30b47-2577-43ad-9095-1861d318dc05" KeyPath="yes">
          <CreateFolder />
          <RemoveFolder Id="TARGETDIR.My_Product" On="uninstall" />
        </Component>

      </Directory>

      <Component Id="TARGETDIR" Guid="6fe30b47-2577-43ad-9095-18612df5f80e" KeyPath="yes">
        <CreateFolder />
        <RemoveFolder Id="TARGETDIR" On="uninstall" />
      </Component>

    </Directory>

    <Feature Id="Complete" Title="Complete" Absent="allow" Level="1">
      <ComponentRef Id="My_Product.EmptyDirectory" />
      <ComponentRef Id="Component.Test.exe" />
      <ComponentRef Id="Dir..EmptyDirectory" />
      <ComponentRef Id="TARGETDIR" />
    </Feature>

  </Product>
</Wix>

Thanks again,

@oleg-shilo
Copy link
Owner

oleg-shilo commented Mar 9, 2018

Of course as a brute-force approach you can try to modify the XML in the post-compile WixSharp event. Though, it is truly the last resort here...

The problem is caused by WixSharp trying to lookup the dir for your shortcut and create it if it's not found This algorithm should have solve your problem from the start but it was only design to wotrk for nested dirs not ProgramMenus. And when you started using explicit dir Id for the location of your shortcut you stumbled on another deficiency of this algorithm. This is the problem and it needs to be fixed.

As a work around you can try to use path for the location value but you will need to add ProgramMenu dir anyway. This is what we started with.

The following code seems to work:

var project =
        new Project("My Product",
            new Dir(new Id("INSTALL_DIR"), @"%ProgramFiles%\1My Product",
                new WixSharp.File(@"myapp.exe",
                    new FileShortcut("My App", @"%ProgramMenu%\1My Product") { Advertise = true })),
            new Dir(@"%ProgramMenu%\1My Product"));

Though I am elevating it to a bug as it needs a proper addressing.

@oleg-shilo oleg-shilo added the bug label Mar 9, 2018
oleg-shilo added a commit that referenced this issue Mar 9, 2018
…erted by the 'start digit in the name' check for composite Dir Ids (e.g. ProgramMenuFolder.1My_Product);
oleg-shilo added a commit that referenced this issue Mar 9, 2018
…ich now can be both dir id and dir path. Triggered by #307
@oleg-shilo
Copy link
Owner

The latest fixes addressed the problems we discussed. All of them, including that "cosmetic underscore".

If you please test it with the binaries from the git. If not, then wait a bit for the next HotFix release I just don't feel like the code base is ready for an immediate release yet.

@naloney
Copy link
Author

naloney commented Mar 9, 2018

Hi Oleg,

Thanks again for your help, I'll try to find time to test this today or over the weekend!

@oleg-shilo
Copy link
Owner

And this is whare yu can get the all binaries: https://github.com/oleg-shilo/wixsharp/tree/master/Source/src/WixSharp.Samples

oleg-shilo added a commit that referenced this issue Apr 11, 2018
-----

v1.6.4.0
* Implemented localization of the Feature's description text (ManagedUI)
* AutoEllipsis enabled for long Feature's label text
* Issue #337: How to set ManagedAction execute before Appsearch
  Added `SetupEventArgs.ManagedUI.Shell.CustomErrorDescription`
* Add dual signing of binaries.
* Issue #333: FeaturesDialog drawing issues
* Added sample for "Issuae #324: Capture Msi error in bootraper"
* Added support for bootstrapper generic items (`Bundle.Items`): Triggered by #315
* Issue #270: Deduplication of files added with wildcards sample dedup
* Added additional resolution algorithm for FileShortcut location, which now can be both dir id and dir path. Triggered by #307
* Issue #307: Fileshortcuts starting with an integer; Removed '_' inserted by the 'start digit in the name' check for composite Dir Ids (e.g. ProgramMenuFolder.1My_Product);
* Issue #180: Multiple root level directories. Added `InstallDir` class
* Added `FileShortcut` constructor that does nor require 'location' parameter. It automatically creates the shortcut in the parent `Dir`.  Triggered by #307.
* Package WixSharp.bin: added copying nbsbuilder.exe to output folder
* NuGet package(s): Added <references> section to register only managed dlls.
  WixSharp.bin.targets file is added to copy unmanaged dll to the output folder.
* Implementation XmlFile Element
* Issue #304: Localization bootstrapper via theme file
* Added optional generation of the XML id attribute for `Bootstrapper.Payload.Id`
* Issue #303: ExternalTool method ConsoleRun(Action<string> onConsoleOut) pass invalid unicode strings to onConsoleOut
* Added `ExternalTool,Encoding`. The default value is `Encoding.UTF8`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants