rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Bootstrapper doesn't work on Far Eastern computers

Dear All,

This is another issue we've found when investigating problem described in post http://www.advancedinstaller.com/forums ... php?t=4986.

This time the problem is that bootstrapper doesn't work when all of the following conditions are met:
  • the installer is invoked by another application which passes short (MS-DOS) path to the installer (.exe) when calling CreateProcess
  • the installer is called from a directory which has wide characters in its path.
To reproduce:
  • Create a default Enterprise project with AI 6.1 (in my case project language was set to English(UK))
  • Configure it to add any file to Application Directory
  • In Media->Media select Archive installation files into CAB files
  • Make sure that in Bootstrapper tab Create EXE setup file and Include install files in EXE are selected.
  • build project
  • put the installer to directory which has wide characters in the path (I've tried it on a Japanese Windows XP; I've put the installer onto Desktop of user イル)
  • run the following application passing it S as first parameter and full, long path to the installer as second parameter:

    Code: Select all

    #include <TCHAR.h>
    #include <windows.h>
    #include <vector>
    #include <iostream>
    #include <string>
    
    int wmain(int argc, wchar_t* argv[])
    {
      if ( 3 != argc ) {
        std::cerr << "Parameters: " << std::endl;
        std::cerr << "  L | S - first parameter (case sensitive) saying whether CreateProcess should be called with Short od Long Path" << std::endl;
        std::cerr << "  <path> - second parameter should be full path to the installer" << std::endl;
        return -1;
      }
    
      STARTUPINFOW startupInfo = {0};
      startupInfo.cb = sizeof(startupInfo);
      PROCESS_INFORMATION processInformation = {0};
    
      if ( L'S' == argv[1][0] ) {  
        std::vector<wchar_t> shortInstallerPath( 1, L'\0' ); // set size to 1 to allow &shortInstallerPath[0] call
        const DWORD shortInstallerPathLength = GetShortPathNameW( argv[2], &shortInstallerPath[0], (DWORD) shortInstallerPath.size() );
    
        if ( shortInstallerPathLength > shortInstallerPath.size() ) {
          shortInstallerPath.resize( shortInstallerPathLength + 1, L'\0' ); // + 1 to allow for terminating NULL char
        } 
    
        if ( GetShortPathNameW( argv[2], &shortInstallerPath[0], (DWORD) shortInstallerPath.size() ) != 0 ) {
          std::cout << "Calling CreateProcess with short path" << std::endl;
          if ( CreateProcessW(NULL, &shortInstallerPath[0], NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation ) == 0 ) {
            std::cerr << "CreateProcess failed with error code: " << GetLastError() << std::endl;
            return -1;
          }
        } else {
          std::cerr << "GetShortPathName failed with error code: " << GetLastError() << std::endl;
          return -1;
        }
      } else if ( L'L' == argv[1][0] ) {
        std::cout << "Calling CreateProcess with long path" << std::endl;
        if ( CreateProcessW(NULL, argv[2], NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation ) == 0 ) {
          std::cerr << "CreateProcess failed with error code: " << GetLastError() << std::endl;
          return -1;
        }
      } else {
        std::cerr << "Incorrect first parameter." << std::endl;
      }
    
      return 0;
    }
    
    
Effect:
  • Instead of installation you'll see msiexec help dialog box
We've found this problem as we were copying installer to the TEMP directory and function GetTempPath returns short paths :-(.

This problem can be easily worked around by using long paths (use the app shown with L as first parameter). I'm raising this problem as it may be symptom of more dangerous bug.

Would you be so kind as to confirm that using long paths is good enough workaround for this problem.

Thank you so much,
Rafal
Cosmin
Posts: 5797
Joined: Tue Jul 10, 2007 6:39 pm
Contact: Website

Hi,

We have tested this but we cannot reproduce the behavior you encountered. Using the code you provided we can launch the bootstrapper from another application and use short paths.

Please try this on another machine.

Regards,
Cosmin
Cosmin Pirvu - Advanced Installer Team
Follow us: Twitter - Facebook - YouTube
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,

I've tried this on another PC. I've installed Japanese Windows XP SP2, installed all recommended updates from Windows Update and:
  • Copied the installer (sample.exe - description on how to create it are in my original post) onto Desktop of a user which has Japanese username (with some Japanese chars in it).
  • Ran the application (TestSetup.exe which source code is in the original post):

    Code: Select all

    TestSetup.exe S "C:\Documents and Settings\サダィ\デスクトップ\sample.exe"
and got Windows Installer help dialog instead of normal installer dialogs.

In reproducing it is very important that you try it on Japanese Windows XP with sample.exe in a folder which path contains Japanese characters.

Do you think you could try to reproduce the problem again?

Thank you,
Rafal
Cosmin
Posts: 5797
Joined: Tue Jul 10, 2007 6:39 pm
Contact: Website

Hi,

Please note that the problem is caused by the code you are using. You can test this by adding the short path as a constant in your code and use it to launch the bootstrapper (most likely the short path is not resolved correctly by the code).

Regards,
Cosmin
Cosmin Pirvu - Advanced Installer Team
Follow us: Twitter - Facebook - YouTube
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,

I've chopped the program massively:

Code: Select all

#include <TCHAR.h>
#include <windows.h>
#include <vector>
#include <iostream>
#include <string>

int wmain(int argc, wchar_t *argv[]) 
{
  wchar_t path[] = L"C:\\DOCUME~1\\サダィ\\LOCALS~1\\Temp\\sample.exe";

  STARTUPINFOW startupInfo = {0};
  startupInfo.cb = sizeof(startupInfo);
  PROCESS_INFORMATION processInformation = {0};

  if ( CreateProcessW(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation ) == 0 ) {
    std::cerr << "CreateProcess failed with error code: " << GetLastError() << std::endl;
    return -1;
  }

  return 0;
}
and I still get the same problem. This time I've:
  • copied the installer (sample.exe) to the TEMP directory (which on the test machine was configured to be "C:\Documents and Settings\サダィ\Local Settings\Temp".
  • got the short path to the TEMP dir by looking at system environment variable TEMP.
  • hardcoded path to the installer in the TEMP directory in the application above.
  • compiled the program and ran it on the test machine.
As I mentioned. The problem happened again and this time no short path resolution took place.

Can you test this app on your machines?

BTW. Could you tell me what exactly do you think is wrong with the code from my original post? I believe that it was ok.

Thanks so much,
Rafal
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,

I've just found out that when you call CreateProcess with path in first argument rather than second then the installer works fine:

Code: Select all

CreateProcessW(path, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation )
However, this way of creating installer has caused us other problems. Also both ways are valid so I imagine that applications should work correctly in both situations (unless they're are parsing command line args in some strange way).

Is there anything unusual in the way the bootstrapper parses command line arguments?

Regards,
Rafal
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,

I've spoken to my colleague which started using CreateProcess with path as a second argument. Apparently he hasn't got much choice as according to Microsoft (http://support.microsoft.com/kb/175986) using only second argument or both arguments (cases 2 and 3) are the only ways of passing some command line options to the application that is being invoked.

I also wonder if you get anywhere with your attempts to reproduce the problem? Please let me know if you have any problems and I'll do my best to give you as many info as possible.

Thank you so much,
Rafal
Cosmin
Posts: 5797
Joined: Tue Jul 10, 2007 6:39 pm
Contact: Website

Hi,

Here is the code we used to test the use of wide characters in the

Code: Select all

bootstrapper path:
#include "stdafx.h"

#include <TCHAR.h>
#include <windows.h>
#include <vector>
#include <iostream>
#include <string>

int wmain(int argc, wchar_t* argv[])
{
  if ( 3 != argc ) {
    std::cerr << "Parameters: " << std::endl;
    std::cerr << "  L | S - first parameter (case sensitive) saying whether CreateProcess should be called with Short od Long Path" << std::endl;
    std::cerr << "  <path> - second parameter should be full path to the installer" << std::endl;
    return -1;
  }

  STARTUPINFOW startupInfo = {0};
  startupInfo.cb = sizeof(startupInfo);
  PROCESS_INFORMATION processInformation = {0};

  argv[2] = L"D:\\イル\\Test Setup Extraction Folder.exe";
  if ( L'S' == argv[1][0] ) { 
    std::vector<wchar_t> shortInstallerPath( 1, L'\0' ); // set size to 1 to allow &shortInstallerPath[0] call
    const DWORD shortInstallerPathLength = GetShortPathNameW( argv[2], &shortInstallerPath[0], (DWORD) shortInstallerPath.size() );

    if ( shortInstallerPathLength > shortInstallerPath.size() ) {
      shortInstallerPath.resize( shortInstallerPathLength + 1, L'\0' ); // + 1 to allow for terminating NULL char
    }

    if ( GetShortPathNameW( argv[2], &shortInstallerPath[0], (DWORD) shortInstallerPath.size() ) != 0 ) {
      std::cout << "Calling CreateProcess with short path" << std::endl;
      if ( CreateProcessW(NULL, &shortInstallerPath[0], NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation ) == 0 ) {
        std::cerr << "CreateProcess failed with error code: " << GetLastError() << std::endl;
        return -1;
      }
    } else {
      std::cerr << "GetShortPathName failed with error code: " << GetLastError() << std::endl;
      return -1;
    }
  } else if ( L'L' == argv[1][0] ) {
    std::cout << "Calling CreateProcess with long path" << std::endl;
    if ( CreateProcessW(NULL, argv[2], NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation ) == 0 ) {
      std::cerr << "CreateProcess failed with error code: " << GetLastError() << std::endl;
      return -1;
    }
  } else {
    std::cerr << "Incorrect first parameter." << std::endl;
  }

  return 0;
}
You can test it to see if it works (it's your code and it uses a constant for the path with wide characters).

I'm not sure why your code doesn't work correctly and I'm afraid that we do not offer support for creating custom code. However, I have increased the priority of the feature which will allow you to use paths with wide characters for the bootstrapper).

Regards,
Cosmin
Cosmin Pirvu - Advanced Installer Team
Follow us: Twitter - Facebook - YouTube
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,

Thanks for your time. Unfortunately the code that you've sent exhibits the same problem. :-(
I'm not sure why your code doesn't work correctly and I'm afraid that we do not offer support for creating custom code.
I totally understand that, but the code posted by me on Tue Jan 22, 2008 9:22 am does what it is supposed to do (run specified application). The problem is that the application being run (the installer) behaves in unusual way (displays Windows Installer help dialog instead of installing the application). As I don't have source code for this application (the bootstrapper) I cannot really tell what in the call to CreateProcess makes it go wrong. Although I found out that using long paths seems to remove the immediate problem I might have been just lucky and this solution may not be sufficient to work around the problem in all cases. In this situation I thought that it was best to ask you as the creators of the application which behaves differently depending on how it is started.

I hope you will understand me asking for your help especially that other applications that I tried to start using the test app (which code is in previous posts) run without any problems no matter how I called them.

As I understand you haven't manage to reproduce the problem so maybe we're using different installers. I can send you the test aip file with which I can reproduce the problem.

Best wishes,
Rafal
Cosmin
Posts: 5797
Joined: Tue Jul 10, 2007 6:39 pm
Contact: Website

Hi,
Unfortunately the code that you've sent exhibits the same problem.
Perhaps there is a problem on your computer. Can you reproduce this on other machines? Note that this code works on all our test machines when you use a constant predefined path.
As I don't have source code for this application (the bootstrapper) I cannot really tell what in the call to CreateProcess makes it go wrong.
Please note that the bootstrapper uses only one function: the GetModuleFileName() function to determine it's path and to perform the extraction.

Also, the command line used by the bootstrapper is the one received by the main function:

Code: Select all

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /hPrevInstance/, LPTSTR lpstrCmdLine, int /nCmdShow/)
Please try using the short path of the bootstrapper (you can see the short path by using the dir /x command). Since the short path doesn't contain wide characters (they are encoded automatically) it can be used to launch the bootstrapper.

Regards,
Cosmin
Cosmin Pirvu - Advanced Installer Team
Follow us: Twitter - Facebook - YouTube
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,

I have tried starting the installer from command line using short paths (as displayed by dir /x - and got same problem.

I've also managed to reproduce the problem on another PC.

Maybe the problem lies in the aip file I am using to create the test installer? This is what I have in my aip file:

Code: Select all

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DOCUMENT type="Advanced Installer" CreateVersion="6.1" version="6.1" modules="enterprise" RootPath="." Language="en_GB">
  <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
    <ROW Property="ALLUSERS" Value="2"/>
    <ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]." ValueLocId="*"/>
    <ROW Property="BannerBitmap" Value="default_banner.bmp" Type="1"/>
    <ROW Property="DialogBitmap" Value="default_dialog.bmp" Type="1"/>
    <ROW Property="Manufacturer" Value="Your Company" ValueLocId="*"/>
    <ROW Property="ProductCode" Value="2057:{CB5494AB-B905-4020-AB2E-F3F14D07097E} "/>
    <ROW Property="ProductLanguage" Value="2057"/>
    <ROW Property="ProductName" Value="Your Application" ValueLocId="*"/>
    <ROW Property="ProductVersion" Value="1.0.0"/>
    <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
    <ROW Property="UpgradeCode" Value="{20BB9437-85C3-4D59-B1B0-92482415C2B1}"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
    <ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1"/>
    <ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
    <ROW Component="sample.aip" ComponentId="{77A2F8D6-0CC6-49FB-B4EA-FC89022C4CE6}" Directory_="APPDIR" Attributes="0" KeyPath="sample.aip" FullKeyPath="APPDIR"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
    <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="sample.aip"/>
    <ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
    <ROW File="sample.aip" Component_="sample.aip" FileName="sample.aip" Attributes="0" SourcePath="sample.aip" SelfReg="false" Sequence="1"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
    <ROW BuildKey="DefaultBuild" BuildName="DefaultBuild" BuildOrder="1" BuildType="1" InstallationType="4" CabsLocation="1" PackageType="1" FilesInsideExe="true"/>
    <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
    <ROW Path="<ui.ail>"/>
    <ROW Path="<ui_en_GB.ail>"/>
    <ROW Path="<ui_en.ail>"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
    <ROW Fragment="FolderDlg.aip" Path="<FolderDlg.aip>"/>
    <ROW Fragment="StaticUIStrings.aip" Path="<StaticUIStrings.aip>"/>
    <ROW Fragment="UI.aip" Path="<UI.aip>"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
    <ROW Name="aicustact.dll" SourcePath="<aicustact.dll>"/>
    <ROW Name="default_banner.bmp" SourcePath="<default-banner.bmp>"/>
    <ROW Name="default_dialog.bmp" SourcePath="<default-dialog.bmp>"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
    <ATTRIBUTE name="FixedSizeBitmaps" value="2"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
    <ROW Dialog_="FolderDlg" Control_="Back" Event="NewDialog" Argument="WelcomeDlg" Condition="AI_INSTALL" Ordering="1"/>
    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL" Ordering="1"/>
    <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL" Ordering="1"/>
    <ROW Dialog_="FolderDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL" Ordering="3"/>
    <ROW Dialog_="MaintenanceTypeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceWelcomeDlg" Condition="AI_MAINT" Ordering="1"/>
    <ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="2"/>
    <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="1"/>
    <ROW Dialog_="PatchWelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_PATCH" Ordering="3"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
    <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
    <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
    <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
    <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
    <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]"/>
    <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
    <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiInstExSeqComponent">
    <ROW Action="AI_DOWNGRADE" Condition="AI_NEWERPRODUCTFOUND AND (UILevel <> 5)" Sequence="210"/>
    <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=""" Sequence="740"/>
    <ROW Action="AI_STORE_LOCATION" Condition="Not Installed" Sequence="1545"/>
    <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE="No" AND (Not Installed)" Sequence="1300"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
    <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=""" Sequence="740"/>
  </COMPONENT>
  <COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
    <ROW UpgradeCode="[|UpgradeCode]" VersionMax="[|ProductVersion]" Attributes="1025" ActionProperty="OLDPRODUCTS"/>
    <ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
  </COMPONENT>
</DOCUMENT>
I'll try to reproduce the problem on yet another PC. Could you try my .aip file on your machines?

Thank you so much.
Rafal
Cosmin
Posts: 5797
Joined: Tue Jul 10, 2007 6:39 pm
Contact: Website

Hi,

We cannot reproduce this behavior on our test machines with the AIP you sent or with any other AIP.

Let me explain how we are testing this:
- create a folder with wide characters in its name, for example "C:\サダィデスクトップ"
- add to this folder the EXE with the files inside
- when run directly the installation package cannot be run (because of the wide characters)
- go to the command console and set the current directory to C:\
- use the dir /x command to find the shortname of the folder with wide characters
- use this short name to launch the bootstrapper

For example, the bootstrapper is launched with the short path "C:\EFC3~1\setup.exe" instead of "C:\サダィデスクトップ\setup.exe".

Regards,
Cosmin
Cosmin Pirvu - Advanced Installer Team
Follow us: Twitter - Facebook - YouTube
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,
For example, the bootstrapper is launched with the short path "C:\EFC3~1\setup.exe" instead of "C:\サダィデスクトップ\setup.exe".
I think here lies the problem. On my PCs when I create folder c:\サダィデスクトップ\ then its short path is c:\サダィデ~1. I also have the opposite effects. The installer runs ok when invoked by double clicking on it (i.e. with long path), but it gives me msiexec help dialog when invoked from command line using command:

Code: Select all

c:\サダィデ~1\setup.exe
Are you sure you're testing on a Japanese Windows XP with all options in Regional and Language Options set to their default values (i.e. Japanese (日本語) or Japan (日本))?

Kind regards,
Rafal
rzwierz
Posts: 43
Joined: Thu Apr 19, 2007 5:23 pm

Hi Cosmin,
The installer runs ok when invoked by double clicking on it (i.e. with long path), but it gives me msiexec help dialog when invoked from command line using command:
I'm getting the same effects on Japanese Windows Vista Home Premium.

Regards,
Rafal
Cosmin
Posts: 5797
Joined: Tue Jul 10, 2007 6:39 pm
Contact: Website

Hi,

We have tested this on a Japanese Vista machine and you are right, the problem occurs. Please note that in this case there is no solution until Unicode format will be supported by the bootstrapper.

Regards,
Cosmin
Cosmin Pirvu - Advanced Installer Team
Follow us: Twitter - Facebook - YouTube

Return to “Common Problems”