InstallShield用了好几个月了,其便捷的操作,完善的功能很是让人爱不释手,可是用了几个月也发现了些小问题:偶用的
IS12,有时会出现卸载软件后残留开始菜单与桌面图标问题(只是极其个别电脑,但这种现象存在),还有的就是在更改几次安装程序后原安装程序的残留信息,某些情况下会对新生成的安装文件运行产生干扰,因此需要在新安装程序运行时强制删除残留信息(不是卸载,已经卸载过)。现将解决这些小问题的思路整理如下:
1)开始菜单及桌面图标残留
可以用 LaunchAppAndWait来运行 cmd进行文件夹及文件的删除,也可以用 IS自带的 DeleteProgramFolder和 DeleteFolderIcon进行显示删除,这里记住几个常量以对特殊文件夹路径进行读取
WINSYSDIR或 SystemFolder读取 %windir%\system32文件夹 (如 c:\windows\system32)
WINDIR或 WindowsFolder读取 %windir%文件夹 (如 c:\windows)
FOLDER_DESKTOP或 DesktopFolder读取桌面文件夹
INSTALLDIR或 TARGETDIR读取文件安装路径
FOLDER_PROGRAMS或 ProgramMenuFolder读取程序菜单文件夹
@PRODUCT_NAME获取安装程序名称
@PRODUCT_GUID获取安装程序的 ID
一般来说这几个常量就能够满足我们的操作了,如果还要了解更多,可以在 <ISProductFolder>\Script\isrt\Include\SysVars.h和 <ISProductFolder> Script\iswi\Include\SysVarsConv.h,记不住? IS的安装文件里面搜 .h文件,找一个你知道的关键字,总不会记一个关键字也难吧?
因此 DeleteFile ( FOLDER_DESKTOP^@PRODUCT_NAME+".lnk" );和 DeleteProgramFolder ( FOLDER_PROGRAMS^@PRODUCT_NAME );这两句话加在 On MaintUIAfter()里就可满足清除残留图标的要求了。
2)自定义快捷方式
IS自带有向导来让你指定快捷方式及其图标,但是这样一来必须把你指定的每一个图标作为一个资源强加载到安装程序中,在 %windir%\Installer\[ProductCode]文件夹内生成 N个 EXE或 ICO文件,二来也不能根据系统本身的情况作出适当调整,比如你有几个 WORD文档,几个 TXT文档和几个 PDF文档,那么你用指定快捷方式向导的话你就得在上述文件夹内生成 N个图标程序,而不管是否它们中几个文件类型是相同的,更别说某些用户系统类对该类文档的解析程序可能不同,它们的文件图标也可能根本跟你指定的不一样。对于这个问题,当然最灵活的方式就是用脚本添加快捷方式了。
完全从脚本添加快捷方式 :
CreateProgramFolder ( @PRODUCT_NAME );
然后 AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir,
szIconPath, nIcon, szShortCutKey, nFlag);
就可以添加快捷方式到相应的位置了,使用时请注意用 LongPathToQuote处理有空格的路径。这里有一点麻烦的就是查找已知文件的图标关联,起初我想的是找到相应的解析程序,然后从其应用程序里面提取图标。在 IS里面调用一个批处理来获得该 EXE路径,以查找 PDF相应关联程序为例 :
批处理脚本 :
@echo off
setlocal enabledelayedexpansion
echo "%1"
for /f "tokens=2 delims==" %%i in ('assoc .pdf') do (
for /f "tokens=2 delims==" %%j in ('ftype %%i') do (
set "strPath=%%j"
set strPath=!strPath: "%%1"=!
echo !strPath!
echo !strPath!>"%1"
)
)
IS相应脚本 :
szBATFile = SUPPORTDIR^"getPath.bat";
szTXTFile = FOLDER_TEMP^"PDFRdr.txt";
LongPathToQuote( szBATFile, TRUE );
LongPathToQuote( szTXTFile, TRUE );
LaunchAppAndWait ( szBATFile , szTXTFile , LAAW_OPTION_HIDDEN|LAAW_OPTION_WAIT );
if ( FindFile ( FOLDER_TEMP , "PDFRdr.txt" , svResult ) = 0 ) then
OpenFileMode ( FILE_MODE_NORMAL );
OpenFile ( nvFileHandle , FOLDER_TEMP , svResult );
GetLine ( nvFileHandle , svExePath );
CloseFile ( nvFileHandle );
endif;
if ( StrCompare ( svExePath , "" ) = 0 ) then
MessageBox ( "当前未查到有任何 PDF阅读软件 ", SEVERE );
else
MessageBox ( svExePath, INFORMATION );
endif;
将获得的应用程序图径代入 AddFolderIcon,作为图标路径。结果发现诸如 PDF文档的图标未必就在 EXE里面, FoxitReader的图标在 EXE文件里,可 Adobe的 Exe里面只有一个图标, PDF文档图标根本没有,没办法,还只有从注册表一途来查找了 :
/*****************************
*取得系统 PDF文件关联类型图标
*****************************/
function STRING GetPDFIcon()
NUMBER nStart, nvType, nvSize;
STRING szStart;
STRING svResult, svValue, svKey;
STRING svIconPath;
NUMBER nvIconIndex;
begin
RegDBSetDefaultRoot ( HKEY_CLASSES_ROOT );
svKey = ".pdf";
if ( RegDBKeyExist ( svKey ) = 1 ) then
RegDBGetKeyValueEx ( svKey , "" , nvType , svValue , nvSize );
svKey = svValue;
if ( RegDBKeyExist ( svKey ) = 1 ) then
//子键存在 ,查找 DefaultIcon,如果有 ,读取 ,如果没有 ,读取它的 Version,再读注册表
if ( RegDBKeyExist ( svKey + "\\DefaultIcon" ) = 1 ) then
RegDBGetKeyValueEx ( svKey + "\\DefaultIcon" , "" , nvType , svValue, nvSize );
return svValue;
else
//未发现关联类型的 DefaultIcon键 ,则查找其 CurVer键 ,查其版本号
if ( RegDBKeyExist ( svKey + "\\CurVer" ) = 1 ) then
RegDBGetKeyValueEx( svKey + "\\CurVer", "" , nvType, svValue, nvSize );
svKey = svValue;
if ( RegDBKeyExist ( svKey ) = 1 ) then
if ( RegDBKeyExist ( svKey + "\\DefaultIcon" ) = 1 ) then
RegDBGetKeyValueEx( svKey + "\\DefaultIcon" , "" , nvType, svValue, nvSize );
return svValue;
else
return "";
endif; //endif check ICON value
else
return "";
endif; //endif check CurVer value
else
return "";
endif; //endif check CurVer Exist
endif; //endif check DefaultIcon Exist
endif; //end if check .pdf filetype exist
endif; //endif check .pdf key exist
end;
然后在 On FirstUIAfter()里面调用该函数取得文件关联路径
:
//查找 PDF文件关联 ,并取得关联类型图标
svDefaultIcon = GetPDFIcon();
if ( StrCompare ( svDefaultIcon , "" ) != 0 ) then
nStart = StrFind ( svDefaultIcon , "," );
if ( nStart > 0 ) then
StrSub ( svPDFIconPath , svDefaultIcon , 0 , nStart );
StrSub ( svPDFIconIndex , svDefaultIcon , nStart+1 , 10 );
//MessageBox ( "图标路径为 :" + svPDFIconPath + " 索引号为 :" + svPDFIconIndex , INFORMATION );
endif;
StrToNum ( nvPDFIconIndex , svPDFIconIndex );
else
svPDFIconPath = WINSYSDIR^"shell32.dll";
nvPDFIconIndex = 0;
endif;
svResult = "";
nvResult = FindAllFiles ( INSTALLDIR , "*.pdf" , svResult , RESET );
while ( !nvResult )
LongPathToQuote ( svResult , TRUE );
ParsePath ( svPDFFileName , svResult , FILENAME_ON LY );
AddFolderIcon ( FOLDER_PROGRAMS^@PRODUCT_NAME ,
svPDFFileName ,
svResult ,
"" ,
svPDFIconPath , nvPDFIconIndex ,
"" ,
REPLACE );
nvResult = FindAllFiles ( INSTALLDIR , "*.pdf" ,svResult , CONTINUE );
endwhile;
最后,添加卸载的快捷方式,就基本上大功告成了。
//添加卸载快捷方式
nStart = StrFind ( UNINSTALL_STRING , ".exe" );
if ( nStart >= 0 ) then
StrSub ( szUninstPath , UNINSTALL_STRING , 0 , nStart+4 );
LongPathToQuote ( szUninstPath , FALSE );
StrSub ( szUninstParam, UNINSTALL_STRING , nStart+4 , 200 );
LongPathToQuote ( szUninstParam, FALSE );
endif;
AddFolderIcon ( FOLDER_PROGRAMS^@PRODUCT_NAME ,
"卸载 " + @PRODUCT_NAME ,
"\"" + szUninstPath + "\"" + szUninstParam ,
"" ,
INSTALLDIR^"Uninstall.ico" ,
0 ,
"" ,
REPLACE );
3)残留安装信息的删除
目前就发现一个 IS的安装信息在 [ProgramFilesFolder]\InstallShield Installation Information\[ProcuctCode]和 [WindowsFolder]\Installer\[ProductCode]里面有相关的文件,于是安装之前先检测此两处文件夹是否存在,清除之,避免残留信息的干扰。
/*****************************
*旧版残留信息清除
*****************************/
function NUMBER DealOldEdition()
STRING szPath;
begin
//删除 InstallSheild Installation Information信息
szPath = PROGRAMFILES^"InstallShield Installation Information"^PRODUCT_GUID;
if ( ExistsDir ( szPath ) = 0 ) then
if (LaunchAppAndWait( WINSYSDIR^"cmd.exe", "/c rd /s/q \""+szPath+"\"", LAAW_OPTION_WAIT | LAAW_OPTION_HIDDEN) = 0) then
//MessageBox( "删除文件夹 "+szPath+"成功 ", INFORMATION );
else
return 0;
//MessageBox( "删除文件夹 "+szPath+"失败 ", INFORMATION );
endif;
endif;
//删除 Installer信息
szPath = WINDIR^"Installer"^PRODUCT_GUID;
if ( ExistsDir ( szPath ) = 0 ) then
if (LaunchAppAndWait( WINSYSDIR^"cmd.exe", "/c rd /s/q \""+szPath+"\"", LAAW_OPTION_WAIT | LAAW_OPTION_HIDDEN) = 0) then
//MessageBox( "删除文件夹 "+szPath+"成功 ", INFORMATION );
else
return 0;
//MessageBox( "删除文件夹 "+szPath+"失败 ", INFORMATION );
endif;
endif;
return 1;
end;
1)开始菜单及桌面图标残留
可以用 LaunchAppAndWait来运行 cmd进行文件夹及文件的删除,也可以用 IS自带的 DeleteProgramFolder和 DeleteFolderIcon进行显示删除,这里记住几个常量以对特殊文件夹路径进行读取
WINSYSDIR或 SystemFolder读取 %windir%\system32文件夹 (如 c:\windows\system32)
WINDIR或 WindowsFolder读取 %windir%文件夹 (如 c:\windows)
FOLDER_DESKTOP或 DesktopFolder读取桌面文件夹
INSTALLDIR或 TARGETDIR读取文件安装路径
FOLDER_PROGRAMS或 ProgramMenuFolder读取程序菜单文件夹
@PRODUCT_NAME获取安装程序名称
@PRODUCT_GUID获取安装程序的 ID
一般来说这几个常量就能够满足我们的操作了,如果还要了解更多,可以在 <ISProductFolder>\Script\isrt\Include\SysVars.h和 <ISProductFolder> Script\iswi\Include\SysVarsConv.h,记不住? IS的安装文件里面搜 .h文件,找一个你知道的关键字,总不会记一个关键字也难吧?
因此 DeleteFile ( FOLDER_DESKTOP^@PRODUCT_NAME+".lnk" );和 DeleteProgramFolder ( FOLDER_PROGRAMS^@PRODUCT_NAME );这两句话加在 On
2)自定义快捷方式
IS自带有向导来让你指定快捷方式及其图标,但是这样一来必须把你指定的每一个图标作为一个资源强加载到安装程序中,在 %windir%\Installer\[ProductCode]文件夹内生成 N个 EXE或 ICO文件,二来也不能根据系统本身的情况作出适当调整,比如你有几个 WORD文档,几个 TXT文档和几个 PDF文档,那么你用指定快捷方式向导的话你就得在上述文件夹内生成 N个图标程序,而不管是否它们中几个文件类型是相同的,更别说某些用户系统类对该类文档的解析程序可能不同,它们的文件图标也可能根本跟你指定的不一样。对于这个问题,当然最灵活的方式就是用脚本添加快捷方式了。
完全从脚本添加快捷方式 :
CreateProgramFolder ( @PRODUCT_NAME );
然后 AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir,
szIconPath, nIcon, szShortCutKey, nFlag);
就可以添加快捷方式到相应的位置了,使用时请注意用 LongPathToQuote处理有空格的路径。这里有一点麻烦的就是查找已知文件的图标关联,起初我想的是找到相应的解析程序,然后从其应用程序里面提取图标。在 IS里面调用一个批处理来获得该 EXE路径,以查找 PDF相应关联程序为例 :
批处理脚本 :
@echo off
setlocal enabledelayedexpansion
echo "%1"
for /f "tokens=2 delims==" %%i in ('assoc .pdf') do (
for /f "tokens=2 delims==" %%j in ('ftype %%i') do (
set "strPath=%%j"
set strPath=!strPath: "%%1"=!
echo !strPath!
echo !strPath!>"%1"
)
)
IS相应脚本 :
szBATFile = SUPPORTDIR^"getPath.bat";
szTXTFile = FOLDER_TEMP^"PDFRdr.txt";
LongPathToQuote( szBATFile, TRUE );
LongPathToQuote( szTXTFile, TRUE );
LaunchAppAndWait ( szBATFile , szTXTFile , LAAW_OPTION_HIDDEN|LAAW_OPTION_WAIT );
if ( FindFile ( FOLDER_TEMP , "PDFRdr.txt" , svResult ) = 0 ) then
OpenFileMode ( FILE_MODE_NORMAL );
OpenFile ( nvFileHandle , FOLDER_TEMP , svResult );
GetLine ( nvFileHandle , svExePath );
CloseFile ( nvFileHandle );
endif;
if ( StrCompare ( svExePath , "" ) = 0 ) then
MessageBox ( "当前未查到有任何 PDF阅读软件 ", SEVERE );
else
MessageBox ( svExePath, INFORMATION );
endif;
将获得的应用程序图径代入 AddFolderIcon,作为图标路径。结果发现诸如 PDF文档的图标未必就在 EXE里面, FoxitReader的图标在 EXE文件里,可 Adobe的 Exe里面只有一个图标, PDF文档图标根本没有,没办法,还只有从注册表一途来查找了 :
/*****************************
*取得系统 PDF文件关联类型图标
*****************************/
function STRING GetPDFIcon()
NUMBER nStart, nvType, nvSize;
STRING szStart;
STRING svResult, svValue, svKey;
STRING svIconPath;
NUMBER nvIconIndex;
begin
RegDBSetDefaultRoot ( HKEY_CLASSES_ROOT );
svKey = ".pdf";
if ( RegDBKeyExist ( svKey ) = 1 ) then
RegDBGetKeyValueEx ( svKey , "" , nvType , svValue , nvSize );
svKey = svValue;
if ( RegDBKeyExist ( svKey ) = 1 ) then
//子键存在 ,查找 DefaultIcon,如果有 ,读取 ,如果没有 ,读取它的 Version,再读注册表
if ( RegDBKeyExist ( svKey + "\\DefaultIcon" ) = 1 ) then
RegDBGetKeyValueEx ( svKey + "\\DefaultIcon" , "" , nvType , svValue, nvSize );
return svValue;
else
//未发现关联类型的 DefaultIcon键 ,则查找其 CurVer键 ,查其版本号
if ( RegDBKeyExist ( svKey + "\\CurVer" ) = 1 ) then
RegDBGetKeyValueEx( svKey + "\\CurVer", "" , nvType, svValue, nvSize );
svKey = svValue;
if ( RegDBKeyExist ( svKey ) = 1 ) then
if ( RegDBKeyExist ( svKey + "\\DefaultIcon" ) = 1 ) then
RegDBGetKeyValueEx( svKey + "\\DefaultIcon" , "" , nvType, svValue, nvSize );
return svValue;
else
return "";
endif; //endif check ICON value
else
return "";
endif; //endif check CurVer value
else
return "";
endif; //endif check CurVer Exist
endif; //endif check DefaultIcon Exist
endif; //end if check .pdf filetype exist
endif; //endif check .pdf key exist
end;
然后在 On
//查找 PDF文件关联 ,并取得关联类型图标
svDefaultIcon = GetPDFIcon();
if ( StrCompare ( svDefaultIcon , "" ) != 0 ) then
nStart = StrFind ( svDefaultIcon , "," );
if ( nStart > 0 ) then
StrSub ( svPDFIconPath , svDefaultIcon , 0 , nStart );
StrSub ( svPDFIconIndex , svDefaultIcon , nStart+1 , 10 );
//MessageBox ( "图标路径为 :" + svPDFIconPath + " 索引号为 :" + svPDFIconIndex , INFORMATION );
endif;
StrToNum ( nvPDFIconIndex , svPDFIconIndex );
else
svPDFIconPath = WINSYSDIR^"shell32.dll";
nvPDFIconIndex = 0;
endif;
svResult = "";
nvResult = FindAllFiles ( INSTALLDIR , "*.pdf" , svResult , RESET );
while ( !nvResult )
LongPathToQuote ( svResult , TRUE );
ParsePath ( svPDFFileName , svResult , FILENAME_ON
AddFolderIcon ( FOLDER_PROGRAMS^@PRODUCT_NAME ,
svPDFFileName ,
svResult ,
"" ,
svPDFIconPath , nvPDFIconIndex ,
"" ,
REPLACE );
nvResult = FindAllFiles ( INSTALLDIR , "*.pdf" ,svResult , CONTINUE );
endwhile;
最后,添加卸载的快捷方式,就基本上大功告成了。
//添加卸载快捷方式
nStart = StrFind ( UNINSTALL_STRING , ".exe" );
if ( nStart >= 0 ) then
StrSub ( szUninstPath , UNINSTALL_STRING , 0 , nStart+4 );
LongPathToQuote ( szUninstPath , FALSE );
StrSub ( szUninstParam, UNINSTALL_STRING , nStart+4 , 200 );
LongPathToQuote ( szUninstParam, FALSE );
endif;
AddFolderIcon ( FOLDER_PROGRAMS^@PRODUCT_NAME ,
"卸载 " + @PRODUCT_NAME ,
"\"" + szUninstPath + "\"" + szUninstParam ,
"" ,
INSTALLDIR^"Uninstall.ico" ,
0 ,
"" ,
REPLACE );
3)残留安装信息的删除
目前就发现一个 IS的安装信息在 [ProgramFilesFolder]\InstallShield Installation Information\[ProcuctCode]和 [WindowsFolder]\Installer\[ProductCode]里面有相关的文件,于是安装之前先检测此两处文件夹是否存在,清除之,避免残留信息的干扰。
/*****************************
*旧版残留信息清除
*****************************/
function NUMBER DealOldEdition()
STRING szPath;
begin
//删除 InstallSheild Installation Information信息
szPath = PROGRAMFILES^"InstallShield Installation Information"^PRODUCT_GUID;
if ( ExistsDir ( szPath ) = 0 ) then
if (LaunchAppAndWait( WINSYSDIR^"cmd.exe", "/c rd /s/q \""+szPath+"\"", LAAW_OPTION_WAIT | LAAW_OPTION_HIDDEN) = 0) then
//MessageBox( "删除文件夹 "+szPath+"成功 ", INFORMATION );
else
return 0;
//MessageBox( "删除文件夹 "+szPath+"失败 ", INFORMATION );
endif;
endif;
//删除 Installer信息
szPath = WINDIR^"Installer"^PRODUCT_GUID;
if ( ExistsDir ( szPath ) = 0 ) then
if (LaunchAppAndWait( WINSYSDIR^"cmd.exe", "/c rd /s/q \""+szPath+"\"", LAAW_OPTION_WAIT | LAAW_OPTION_HIDDEN) = 0) then
//MessageBox( "删除文件夹 "+szPath+"成功 ", INFORMATION );
else
return 0;
//MessageBox( "删除文件夹 "+szPath+"失败 ", INFORMATION );
endif;
endif;
return 1;
end;