cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
ElenaN
Level 6

Shortcut name with variable

Hi everybody 🙂

I wonder if it’s possible to insert variable into shortcut’s name.

I need to insert [ProductName] property into shortcut name. But simple including [ProductName] in special brackets [] doesn’t work – we will see “[ProductName]” as string instead of expected “My product” (value of [ProductName] property in my project).
I tried to create and use global property [PRODUCT_NAME] and another string {PRODUCT_NAME} – but received same not suitable results.

Help :confused:

BTW, I deal with Basic MSI project
Labels (1)
0 Kudos
4 Replies
DLee65
Level 13

You have to modify the Shortcut table at runtime. I have a script that we run to modify shortcut names at runtime. The shortcut table does not support expanding property values.

IS Custom Action Name: DeLorme_CreateAppShortcuts
Function Name: CreateAppShortcuts
Return Processing: Synchronous
Execution: Immediate
UI Seq: Absent
Install Exec Sequence: After CostFinalize

/*
@brief Creates advertised app shortcuts to reflect product license installed

@arg Handle to MSI DB

@Usage: Modifies both desktop and start menu shortcut name values
and description values.

@bug XMap-2033
@author DanL
@date 3/28/2008
*/
function CreateAppShortcuts(p_hMSI)
STRING sDesc, sProductName, sShortName,
sIconName, sMsg, sQuery, sTempName,
sComponent, sFeature, sDirectory,
sDesktopIcon, strInstalled;
HWND hDB, hView, hRec;
STRING sShortcutData(16);
NUMBER n, nBufferSize, nLen, nDeskIconInstallState,
nDeskIconActionState, nMatch;
begin
nMatch = -1;
//SET shortcut name
n = MsiGetProperty(p_hMSI, "ProductName", sProductName, nBufferSize);
n = MsiGetProperty(p_hMSI, "SHORTNAME", sShortName, nBufferSize);

//SET shortcut description
//Strip "DeLorme " from the Product name. Start position is base zero
nLen = StrLength(sProductName);
StrSub(sProductName, sProductName, 8,nLen);
sDesc = "Launch " + sProductName;
//array is base zero while MSI tables are base 1
sShortcutData(SC_NAMECOLUMN - 1) = sShortName+ "|" + sProductName;
sShortcutData(SC_DESCCOLUMN - 1) = sDesc;

//SET Shortcut icon
//Abort install if icon does not exist in icon table
sIconName = sShortName + "Icon.exe";
if !IconIsFound(p_hMSI, sIconName) then
sMsg = "Shortcut icon is not found.";
SprintfMsiLog(sMsg);
ConditionalMessageBox(p_hMSI, sMsg, SEVERE);
abort;
endif;
sShortcutData(SC_ICONCOLUMN - 1) = sIconName;

//SET Shortcut Component - Find component for app.exe
//Match is case sensitive for sql query, so need to inspect all records and
//do a case insensitive compare
sQuery = "SELECT * FROM `File`";
//TODO: Make a function to replace this block ....
CreateMsiView(p_hMSI, hDB, hView, hRec, sQuery);
repeat
n = MsiViewFetch(hView, hRec);
MsiRecordGetString(hRec, FILE_FILENAME, sTempName, nBufferSize);
nMatch = StrICompare(sTempName, sShortName + ".exe");
if nMatch != 0
then
// Prevent handle leaks
MsiCloseHandle(hRec);
endif;
until (nMatch == 0) || (n != ERROR_SUCCESS);

if n != ERROR_SUCCESS then
sMsg = "Select query failed for " + sShortName + ".exe in the File table.";
SprintfMsiLog(sMsg);
ConditionalMessageBox(p_hMSI, sMsg, SEVERE);
abort;
endif;

MsiRecordGetString(hRec, FILE_COMPONENT, sComponent, nBufferSize);
sShortcutData(SC_COMPONENTCOLUMN - 1) = sComponent;
n = MsiCloseHandle(hRec);
hRec = 0;
n = MsiCloseHandle(hView);
hView = 0;
n = MsiCloseHandle(hDB);
hDB = 0;

//SET shortcut target - advertised shortcuts use the feature name
sQuery = "SELECT * FROM `FeatureComponents` " +
"WHERE `FeatureComponents`.`Component_`='" + sComponent +"'";
//TODO: Make a function to replace this block ....
CreateMsiView(p_hMSI, hDB, hView, hRec, sQuery);
n = MsiViewFetch(hView, hRec);
if n != ERROR_SUCCESS then
sMsg = "Select query failed for " + sComponent + " in the FeatureComponent table.";
SprintfMsiLog(sMsg);
ConditionalMessageBox(p_hMSI, sMsg, SEVERE);
abort;
endif;

MsiRecordGetString(hRec, FC_FEATURE, sFeature, nBufferSize);
sShortcutData(SC_TARGET - 1) = sFeature;
n = MsiCloseHandle(hRec);
hRec = 0;
n = MsiCloseHandle(hView);
hView = 0;
n = MsiCloseHandle(hDB);
hDB = 0;
//will be converted to integer in ShortcutTableEntry function
sShortcutData(SC_SHOWCMDCOLUMN - 1) = "1";
//Need to make sure that this exists in the directory table
//Verify that StartMenuAppFolder1 exists in the Directory Table
sQuery = "SELECT * FROM `Directory` " +
"WHERE `Directory`.`Directory`='StartMenuAppFolder1'";
//TODO: Make a function to replace this block ....
CreateMsiView(p_hMSI, hDB, hView, hRec, sQuery);
n = MsiViewFetch(hView, hRec);
if n != ERROR_SUCCESS then
sMsg = "Select query failed for 'StartMenuAppFolder1' in the Directory table.";
SprintfMsiLog(sMsg);
ConditionalMessageBox(p_hMSI, sMsg, SEVERE);
abort;
endif;
n = MsiCloseHandle(hRec);
hRec = 0;
n = MsiCloseHandle(hView);
hView = 0;
n = MsiCloseHandle(hDB);
hDB = 0;

sShortcutData(SC_DIRECTORYCOLUMN - 1) = "StartMenuAppFolder1";

//Create Start menu app icon
sShortcutData(SC_KEYNAMECOLUMN - 1) = "AppStartMenuSC";
ShortcutTableEntry(p_hMSI,sShortcutData);

//Create Desktop menu app icon (CONDITIONAL)
n = MsiGetFeatureState(p_hMSI, "DesktopShortcut", nDeskIconInstallState, nDeskIconActionState);
if n != ERROR_SUCCESS then
CheckMsiResult(p_hMSI, n, hDB, hView, hRec, sMsg); endif;
if (nDeskIconActionState==INSTALLSTATE_LOCAL) ||
(nDeskIconInstallState==INSTALLSTATE_LOCAL) then
//Verify that StartMenuAppFolder1 exists in the Directory Table
sQuery = "SELECT * FROM `Directory` " +
"WHERE `Directory`.`Directory`='DesktopFolder'";
CreateMsiView(p_hMSI, hDB, hView, hRec, sQuery);
n = MsiViewFetch(hView, hRec);
if n != ERROR_SUCCESS then
sMsg = "Select query failed for 'DesktopFolder' in the Directory table.";
SprintfMsiLog(sMsg);
ConditionalMessageBox(p_hMSI, sMsg, SEVERE);
abort;
endif;
n = MsiCloseHandle(hRec);
hRec = 0;
n = MsiCloseHandle(hView);
hView = 0;
n = MsiCloseHandle(hDB);
hDB = 0;
sShortcutData(SC_DIRECTORYCOLUMN - 1) = "DesktopFolder";

sShortcutData(SC_KEYNAMECOLUMN - 1) = "DesktopSC";
ShortcutTableEntry(p_hMSI, sShortcutData);
endif;

end;




Here are the Shortcut table defines for columns

//Shortcut Table Column definitions
#define SC_KEYNAMECOLUMN 1
#define SC_DIRECTORYCOLUMN 2
#define SC_NAMECOLUMN 3
#define SC_COMPONENTCOLUMN 4
#define SC_TARGET 5
#define SC_ARGSCOLUMN 6
#define SC_DESCCOLUMN 7
#define SC_HOTKEYCOLUMN 8
#define SC_ICONCOLUMN 9
#define SC_ICONINDEXCOLUMN 10
#define SC_SHOWCMDCOLUMN 11
#define SC_WKDIRCOLUMN 12
#define SC_DISPRESCDLLCOL 13
#define SC_DISPRESCIDCOL 14
#define SC_DESCRESCDLLCOL 15
#define SC_DESCRESCIDCOL 16


And here is the function that adds stuff to the shortcut table
/* 
@brief "Modifies" or adds a shortcut table entry

@usage If SC TableName value does not already exist
then the function will add a new record. Otherwise it only modifies
an existing record. It is not possible to modify an active database
so it is necessary to first copy the contents of a matching keyname
to a new record, delete the old record and then insert the new record
as a temporary entry.
@arg p_hMSI Handle to MSI table
@arg p_aShortcutData Array of Shortcut values. MUST be formatted correctly to work.

@since March 28, 2008
@author Danl
@bugs XMAP-2016, 2088
*/
function ShortcutTableEntry (p_hMSI, p_aShortcutData)
HWND hDB, hView, hRec, hNewRec;
NUMBER n, nBufferSize;
NUMBER nFieldCount, i, nTemp;
STRING sQuery, sMsg, sTemp;
STRING sOLD_KeyName, sNEW_KeyName;
begin
i = 1;
//NOTE: The array is base 0 while the shortcut table is base 1
//i is the index to the shortcut table field.
sNEW_KeyName = p_aShortcutData(i-1);

sQuery = "SELECT * FROM `Shortcut` WHERE `Shortcut`.`Shortcut`=" + "'" + sNEW_KeyName + "'";
CreateMsiView(p_hMSI, hDB, hView, hRec, sQuery);
n = MsiViewFetch(hView, hRec);

//TODO: Implement MsiViewGetColumnInfo using MSICOLINFO_NAMES to get dimension of table
nFieldCount = MsiRecordGetFieldCount(hRec);
if nFieldCount = 0 then nFieldCount = 16; endif;

hNewRec = MsiCreateRecord(nFieldCount);
//Get Column Data Type
//hColInfo = MsiCreateRecord(nFieldCount);
if n = ERROR_SUCCESS then
//copy contents of original & check for new data
for i = 1 to nFieldCount
if i == SC_HOTKEYCOLUMN ||
i == SC_ICONINDEXCOLUMN ||
i == SC_SHOWCMDCOLUMN ||
i == SC_DISPRESCIDCOL ||
i == SC_DESCRESCIDCOL
then
nTemp = MsiRecordGetInteger(hRec, i);
//Does array contain data for this field?
if p_aShortcutData(i-1) != "" then
StrToNum(nTemp, p_aShortcutData(i-1)); endif;
n = MsiRecordSetInteger(hNewRec, i , nTemp);
if n < ERROR_SUCCESS then
CheckMsiResult(p_hMSI, n, hDB, hView, hNewRec, sMsg); endif;
else
n = MsiRecordGetString(hRec, i, sTemp, nBufferSize);
if n < ERROR_SUCCESS then
CheckMsiResult(p_hMSI, n, hDB, hView, hNewRec, sMsg); endif;
//Does array contain data for this field?
if p_aShortcutData(i-1) != "" then
sTemp = p_aShortcutData(i-1);
endif;
n = MsiRecordSetString(hNewRec, i, sTemp);
endif;
endfor;
n = MsiViewModify(hView, MSIMODIFY_DELETE, hRec);

n = MsiViewModify(hView, MSIMODIFY_INSERT_TEMPORARY, hNewRec);
if n < ERROR_SUCCESS then
CheckMsiResult(p_hMSI, n, hDB, hView, hNewRec, sMsg); endif;
return 0;
endif;

//Add a new record
for i = 1 to nFieldCount
//check for string or integer data type
if i == SC_HOTKEYCOLUMN ||
i == SC_ICONINDEXCOLUMN ||
i == SC_SHOWCMDCOLUMN ||
i == SC_DISPRESCIDCOL ||
i == SC_DESCRESCIDCOL
then
if p_aShortcutData(i-1) != "" then
StrToNum(nTemp, p_aShortcutData(i-1));
n = MsiRecordSetInteger(hNewRec, i, nTemp);
if n != ERROR_SUCCESS then
CheckMsiResult(p_hMSI, n, hDB, hView, hNewRec, sMsg); endif;
else
n = MsiRecordSetInteger(hNewRec, i, MSI_NULL_INTEGER);
if n != ERROR_SUCCESS then
CheckMsiResult(p_hMSI, n, hDB, hView, hNewRec, sMsg); endif;
endif;
else
n = MsiRecordSetString(hNewRec, i, p_aShortcutData(i-1));
if n != ERROR_SUCCESS then
CheckMsiResult(p_hMSI, n, hDB, hView, hNewRec, sMsg); endif;
endif;
endfor;


sMsg = "Failed to modify database view. \n" +
"MsiViewModify returned: %d\n";

n = MsiViewModify(hView, MSIMODIFY_INSERT_TEMPORARY, hNewRec);
if n < ERROR_SUCCESS then CheckMsiResult(p_hMSI, n, hDB, hView, hRec, sMsg); endif;
MsiCloseHandle(hRec);
hRec = 0;
MsiCloseHandle(hNewRec);
hNewRec = 0;
MsiCloseHandle(hView);
hView = 0;
MsiCloseHandle(hDB);
hDB = 0;
return 0;
end;


Hopefully this is enough of an example to get you going. Someday I should parse through this stuff and create a smaller template that could help people here as this is a commonly requested feature.
0 Kudos
ElenaN
Level 6

It’s the best reply I have ever received on any forums!
Thank you very much
0 Kudos
thepeter
Level 7

I am using a VB script to create combo boxes from arrays stored in properties. Here is the InstallShield guideline I used: http://www.acresso.com/webdocuments/PDF/msiaccess.pdf?link_id=productsTipsTricks
0 Kudos
DLee65
Level 13

The problem with vbscript (and jscript too) is that AV software can identify your calls as a virus. Early on I had tried vbscript. While we never once had problems in the in-house QA testing or beta testing, once it was released to the public we started to see the setup failing.

From that point on we had vowed never never never to used vbscript in any of our products to be released to the public. We can never predict how Anti Virus software will interact with the function calls we make.
0 Kudos