InstallShield 2011 自定义语言选择框

     在使用InstallShield 2011制作安装包的过程中遇到了两个BT问题。第一个问题是InstallShield 2011的语言框的默认值不再匹配"Region and Language Settings"中设置的"Current Language for non-Unicode"。而InstallShield 12是可以的。第二个问题是如果系统没有安装用户所选择的语言会弹出一个错误框,点确定后安装终止。而InstallShield 12在这种情况下可以正常安装,不过界面上的语言显示为乱码。

     我们之前的安装包是用InstallShield 12制作的。现在项目的Dot Net Framework升级到了3.5 SP1,所以得使用支持3.5的InstallShield 2011. 不幸的是客户希望我们的安装包的行为还是和以前一样。

      看了N篇帖子后无果。发邮件给InstallShield的公司,隔了两个礼拜才回信,信的大致意思是2011就是这样的。。。(PS.问他们问题是要收费的。。。)幸亏我早就找到了解决方法。

          关于第一个问题,我们可以自己写一个语言选择框,然后读取系统的Locale语言,设置为默认值。Install Shield的语言选择框是Setup.exe提供的。可以注意一下安装过程,先弹出语言选择框,然后才是解压msi。而我们的代码是打包在msi里面的。所以我们得新建另一个setup.exe,将自定义语言选择框放置在里面,当用户选择了语言后,首先判断系统是否安装了这个语言,如果没有就又弹出语言框让用户选,这就解决了第二个问题。然后就再调用第二个Setup.exe,当然要带上命令行参数 "-l0x0809"。"-l"是命令,后面加上的是用户选择的语言的16位编码。我们的产品有9种语言,为了精简示例代码,我只保留了英语和中文。如果你希望查找语言代码可以打开下面的链接。

           http://msdn.microsoft.com/en-us/library/ms776294(VS.85).aspx

          首先理清这两个安装包。第一个安装包只需要一种语言,我们把自定义的语言选择框放在里面。第二个安装包才是我们真正用来安装的,它是多语言的。

          下面一步步来建立我们的语言框。

          首先在"Dialogs"界面点击右键,选择"New Dialog"。会出现一个Wizard,在Dialog Template界面选择类型"NewScriptBasedDialog"。(千万不要选"Blank Dialog"。这个类型做出来的对话框有个Bug,就是点击窗口上的关闭按钮(右上角那个红叉叉)没有反应。估计是InstallShield的开发人员忘了给它加事件了。)

         将Dialog改名为SelectLanguage。把界面上已有的控件通通删掉,然后加上自己的控件。我是照着InstallShield的语言框做的,因为客户要求一模一样。

        

         只需要一个空界面。多语言是在后台代码里实现的。

         在InstallScript界面新增文件SelectLanguage.rul。下面是代码

          

 

代码
prototype number SdSelectLanguage();
prototype InitDialog(STRING);
#define RES_DIALOG_ID     22011   // "Control identifier" of custom dialog 这是控件的ID,可以到Dialog里自己设置
#define RES_CMB         1301   //   "Control identifier" of combobox  
#define RES_SAVE      1302   //  "Control identifier" of "OK" Button
#define RES_CANCEL    1303   //  "Control identifier" of "Cancel" Button
#define RES_Desc      1304   // "Control identifier" of Title
string selectedText,tempStr;
NUMBER languageIndex;
LIST listDrives;

function number SdSelectLanguage()
    STRING szDialogName, svName; 
    NUMBER nResult, nCmdValue;
    BOOL    bDone;  
    HWND    hwndDlg; 
    number nSize; 
    
string svDrive,languageIndexStr,firstString;
    NUMBER localeLanguageID,i;
    
    begin 
     szDialogName 
= "SelectLanguage"
     nResult 
= EzDefineDialog (szDialogName, """", RES_DIALOG_ID); 
     
if (nResult < 0) then  
        abort; 
     endif; 
     bDone 
= FALSE; 
    
// Loop until done.
    repeat 
        
// Display the dialog and return the next dialog event. 
        nCmdValue = WaitOnDialog (szDialogName);   
        
switch (nCmdValue)  
            
case 2//返回值为2 表示点击了关闭按钮
                 
// The user clicked the window's Close button.                
                 Do(EXIT); 
            
case DLG_ERR:              
                abort; 
            
case DLG_INIT:   
                InitDialog(szDialogName);
            
case RES_CANCEL:
                 Do (EXIT); 
            
case RES_SAVE: 
                  CtrlGetCurSel(szDialogName, RES_CMB,selectedText); 
//获取下拉列表的值
                  ListGetFirstString(listDrives,firstString); //将listDrives的索引置0
                  ListFindString(listDrives,selectedText); //查找,并选中该值
                  ListGetIndex(listDrives,languageIndex); //获取listDrives当前索引              
                 bDone = TRUE;             
        endswitch; 
    until bDone; 
    
// Close the dialog. 
    EndDialog (szDialogName); 
    
// Remove the dialog from memory.
    ReleaseDialog (szDialogName);
    
// If dialog is closed with Next button, display name and company.
     return 0;
end;

//构造界面
function InitDialog(szDialogName)
    number localeLanguageID;
    HWND hwndDlg;
    
string s_Save,s_Cancel,s_desc,s_Title;
          begin
               localeLanguageID
=SYSINFO.nSystemLangID;//系统的locale语言
               listDrives = ListCreate(STRINGLIST);
               hwndDlg 
= CmdGetHwndDlg( szDialogName );              
               
switch(localeLanguageID)
                 
case 1033://English(United States)  这里是十进制的语言代码
                    s_Title="Choose Setup Language";
                    s_desc
="Select the language for the installation from the choices below.";
                    s_Save
="OK";
                    s_Cancel
="Cancel"
                    ListAddString (listDrives, 
"English(US)", AFTER);                 
                    ListAddString (listDrives, 
"Chinese", AFTER); 
                    CtrlSetList(szDialogName, RES_CMB, listDrives); 
                    CtrlSetCurSel(szDialogName, RES_CMB, 
"English(US)");  
                 
case 2052://Chineses(Simplified)
                   s_Title="选择安装的语言";
                    s_desc
="从下面的列表中选择安装的语言";
                    s_Save
="确定";
                    s_Cancel
="取消"
                    ListAddString (listDrives, 
"英语(US)", AFTER);                 
                    ListAddString (listDrives, 
"中文", AFTER);                   
                    CtrlSetList(szDialogName, RES_CMB, listDrives); 
                    CtrlSetCurSel(szDialogName, RES_CMB, 
"中文");                 
                 
default:
                    s_Title
="Choose Setup Language";
                    s_desc
="Select the language for the installation from the choices below.";
                    s_Save
="OK";
                    s_Cancel
="Cancel"
                    ListAddString (listDrives, 
"English(US)", AFTER);                 
                    ListAddString (listDrives, 
"Chinese", AFTER); 
                    CtrlSetList(szDialogName, RES_CMB, listDrives); 
                    CtrlSetCurSel(szDialogName, RES_CMB, 
"English(US)");  
               endswitch;               
               SdSetDlgTitle(szDialogName, hwndDlg, s_Title);
//dialog title                    
               CtrlSetText(szDialogName, RES_Desc,s_desc );//
               CtrlSetText(szDialogName, RES_SAVE, s_Save);     
               CtrlSetText(szDialogName,RES_CANCEL,s_Cancel); 
             
return 0;
          end;

 

           从上面的代码中我们可以看到用户选择的语言存储在全局变量languageIndex中。

           接着我们在Setup.Rul的OnFirstUIBefore事件中添加如下代码。

          

Dlg_SelectLanguage:
    
if !bIsMaintenance then
     
 SdSelectLanguage();    
    endif; 

 

            这段代码用来弹出语言框。我们只需要在安装的时候弹出语言框,修复的时候不需要弹出。所以这里我加了一个判断。但是我并没有用系统的静态变量MAINTENANCE进行判断。因为在这里MAINTENANCE始终为False。这是因为我们有两个安装包,实际上安装的是第二个安装包。所以在第一个安装包里是无法判断MAINTENANCE的。这就需要自己写方法判断是否为修复状态。当然你可以在安装的时候在注册表里写个键值,卸载的时候就把它删掉。这就可以把这个键值作为判断条件。

          我这里提供一个简单的方法,也是用注册表,但不需要自己写额外的键。

 

代码
prototype BOOL IsMaintenance();
function BOOL IsMaintenance()
string regKey;
BOOL bIsMaintenance;
begin
   
if SYSINFO.bIsWow64 then
      regKey
="SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{CD9FTT7B-38C3-4984-9BW5-05DB4E602Y89}";
   
else
      regKey
="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{CD9FTT7B-38C3-4984-9BW5-05DB4E602Y89}";    
   endif;
    RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
    
if(RegDBKeyExist(regKey)<0) then
       bIsMaintenance
=FALSE;
    
else
       bIsMaintenance
=TRUE;
    endif;   
    
return bIsMaintenance;
end;

 

            "CD9FTT7B-38C3-4984-9BW5-05DB4E602Y89"这个值是在第二个安装包的工程里面设置的。在"General Information"这个界面的"Product Code"属性里填入这个值。当然这个值是任意的,你不要填和我一样的值哦,免得我们的产品打架。填了这个值后,InstallShield会为我们自动创建代码中的键值,在卸载的时候会自动删除它。可以看到这个键值在64位和32位的操作系统中路径不同。还有控制面板用的这个键值。

          接下来,在OnEnd事件中,我们通过languageIndex变量得知用户所选的语言,判断系统是否安装了这个语言,如果没有则重新弹出语言框,如果有就调用第二个包。OnEnd中的代码如下:

 

代码
bIsMaintenance=IsMaintenance();//是否为修复
   if !bIsMaintenance then    
      bSupport
=FALSE;
      repeat
         bSupport
= IsSystemSupportLanguage();//是否是系统支持的语言   
         if !bSupport then //系统不支持
            MessageBox("The current operating system does not have the selected language installed. Please select another language to continue.",INFORMATION);
            SdSelectLanguage();
//重新弹出语言选择框
         endif;
      until bSupport;
   endif;  
       
   RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
   szCommand   
=  "Software\\setup.exe"; //第二个包的相对路径 
   szCmdLine
=""
   
if !bIsMaintenance then
    
switch(languageIndex)
      
case 0://English(United States) 英语
        szCmdLine="-l0x0409"
      
case 1://Chineses(Simplified)   中文
        szCmdLine="-l0x0804";       
      
default:   
        szCmdLine 
= "-l0x0409";        
    endswitch;     
   endif;
   
   
if   (LaunchApp(szCommand,   szCmdLine)   <   0)   then //用命令行调用第二个包     
     MessageBox   ("Error",   SEVERE);
     abort;
   endif; 

 

          接下来我要贴出让我饱经磨难的一段代码:IsSystemSupportLanguage--是否是系统支持的语言。我在网上没找到解决方法,最后找了台缺少中文语言包的机器,通过手工查找注册表,看安装语言包前后有哪个键值不同。当然找哪些键值是有规则的,拿语言的代码去搜索,不是一条条去翻。。。虽然找到这个键值并没发多少时间,但是为了一个键值而翻注册表的那种心情,是很沉重的。。。

           最后我发现 HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Control\\Nls\\Locale 下面列出了系统可以支持的所有语言,当然都是编码。如果没有安装某语言,这个语言的键值就为空。代码如下:

 

代码
prototype BOOL IsSystemSupportLanguage();
function BOOL IsSystemSupportLanguage()
BOOL bResult;
STRING szKey,szName,svValue;
NUMBER nvSize,nvType;
begin
  RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
  szKey
="SYSTEM\\ControlSet001\\Control\\Nls\\Locale";
  
switch(languageIndex)
      
case 0://English(United States) 英文
        szName="00000409";
      
case 1://Chineses(Simplified) 中文 
        szName="00000804";
      
default:   
        szName
="00000409";    
  endswitch;
  nvType
=REGDB_STRING;
  
if RegDBGetKeyValueEx(szKey,szName,nvType,svValue,nvSize)<0 then
     bResult
=FALSE;
  
else
     
if svValue=="" then //如果这个键值为空,就表示没有安装
        bResult=FALSE;
     
else
        bResult
=TRUE;// 如果这个键值有值,就表示已安装
     endif;    
  endif;  
  
return bResult;
end;

         下面放出工程文件。/Files/linjiao0125/LanguageDialogProject.rar

          如果有疑问可以加我的QQ:252945226。 如果你有更好的解决方法请留言,或者加QQ,或者发邮件 linjiao0125@qq.com

 

 

posted on 2010-10-08 18:13  林骄  阅读(5446)  评论(0)    收藏  举报