Wednesday, December 29, 2010

Checking the version of Windows Installer on a machine.

Often, we need to know what version of windows Installer is available on a machine. Valid scenario's are like if we want some features of our installshield product specific to a Windows Installer version.


For this, go to Windows\System 32 folder. Look for MSI.DLL and check its version. If the version is 3.1.4000.2435, you have the latest version.


One common point of confusion is that even if you have the latest version of Windows Installer 3.1 on your system and you type in msiexec.exe /? from a command-window, you will still be told that you are on version 3.1.4000.1823 or 3.1.4000.1830. This is because msiexec.exe /? will only give you the version of msiexec on the system -- not the other Windows Installer-related dll's. (The version of msiexec was not updated to 3.1.4000.2435 with the (v2) redistributable, just msi.dll was updated.)"


VB Script:

I found a neat .vbs browser hosted script fo this. Save the following code as .htm
<html>
<head>
<script language="vbscript">
sub document_onclick()
set installer = createobject("windowsinstaller.installer")
msgbox installer.version
end sub
</script>
</head>
<body>
Click me for Windows Installer version...
</body>
</html>


and you are done:-). Note that the "windowsinstaller.installer" object is not marked safe for scripting in IE browser hosted script... Double click the saved htm file and then you need to click the information bar to allow the blocked ActiveX control in IE.

Friday, October 15, 2010

Validate Windows Service User Account

Hi Friends, in many of my projects, we need to create windows service, ask credentials for running the service. I spent a lot of time in my initial stage of profession for implementing it according to the choice of the user.


Scenario:
There was a scenario, where user needs to put a choice by means of installer, whether he needs to install an run the services on behalf of Local Account, or by entering the Windows Domain Account credentials for running the service.


Solution:
I write a Installscript custom action after a lot of R&D on net for the scenario when we ask custom credentials by user.


Description:
ValidateWindowsServiceUserAccount() -- custom function which we need to call from our script.
gsServiceAccountUserName -- global variable which i had used to store the credentials which were a mix of Domain + Windows account e.g. Domain\John.Natham.


function ValidateWindowsServiceUserAccount()
    OBJECT objDSO, objDomain;
    LIST listID;
    NUMBER iIndex, nResult;
    STRING sArray(), svString, strDomain, strWinServiceUser,szMsg;
begin
try
   set objDSO = CoGetObject("WinNT:","");
   if (!IsObject(objDSO)) then
      //MessageBox("Object NOT Created", 0);
   else
      //Break User name in domain and user
      listID = ListCreate (STRINGLIST);      
      if (StrGetTokens (listID, gsServiceAccountUserName, "\\") < 0) then
         //Error
      else
         //Success
         iIndex=0;
         nResult = ListGetFirstString(listID, svString);
         while (nResult != END_OF_LIST)
              //nitems=nitems+1;
              Resize(sArray,iIndex+1);
              sArray(iIndex)=svString;
              iIndex = iIndex+1;
              nResult = ListGetNextString (listID, svString);
         endwhile;
      endif;      
      ListDestroy (listID);
      strDomain = sArray(0);
      strWinServiceUser = sArray(1);
      //
      set objDomain = objDSO.OpenDSObject("WinNT://" + strDomain,
                            strWinServiceUser, gsServiceAccountPassword, 1);

      gbValidWinServiceUser = TRUE;
      //set objDSO = Nothing
      //set objDomain = Nothing
   endif;
catch
    gbValidWinServiceUser = FALSE;
    //MessageBoxEx(Err.Number,"",INFORMATION);
   switch (Err.Number)
       case "-2147023570":
            szMsg = "Invalid user name or password.";
            ListAddString(gLIST_VALIDATION_ERROR,"- "+szMsg,AFTER);
            //MessageBoxEx("Invalid user name or password.","",SEVERE);
       case "-2147022987":
            szMsg = "User account is locked.";
            ListAddString(gLIST_VALIDATION_ERROR,"- "+szMsg,AFTER);
            //MessageBoxEx("User account is locked.","",SEVERE);
       case "-2147023565":
            szMsg = "User account is disabled.";
            ListAddString(gLIST_VALIDATION_ERROR,"- "+szMsg,AFTER);
       default:
            szMsg = "Unknown error, unable to verify credentials.";
            ListAddString(gLIST_VALIDATION_ERROR,"- "+szMsg,AFTER);
    endswitch;
endcatch;
end;

Tuesday, September 28, 2010

Check if a windows process is running...

Hello Friends,
     During one of my project a requirement comes across to know whether a process is currently running on your machine or not. I looked across a number of sites, but none proved to be useful to me. But in the last I got some useful scripts which proved to be very efficient when applied through installscipt code.

The below script is in vb, which you could use through vbscript type custom action in your installshield project. In th variable PROCESS_NAME, you need to replace it by your process name, which you are looking for.

set service = GetObject ("winmgmts:")
for each Process in Service.InstancesOf ("Win32_Process")
      If Process.Name = "PROCESS_NAME" then
          wscript.echo "mmc running"
          wscript.quit             -- Here script will terminate
      End If
next

wscript.echo "mmc not running"         -- This code will execute if process is not running

Tuesday, September 21, 2010

Check if database exist...

Friends, today I have share one of my useful database script which I had implemented in Installshield code to check the existence of a database.

Often we come across scenario, where we either need to create a new database if it does not exist, or to update an existing one. Below script consist of a function named DoesDatabaseExist, which will make a call to the database using all the credentials and database name. I bet, you will find it very useful.

///////////////////////////////////////////////////////////////////////////////
//
// Function: DoesDatabaseExist
//
// Purpose: This function will determine whether a given database exists.
//
// Arguments: svServerName - The name of the SQL server to run the script on
// svDatabaseName - The name of the SQL database to run the script on
// svDriver - ADO requires this, but for SQL server you just send in "SQL Server"
// svUserName - The system account for SQL server
// svUserPassword - The password for the system account
//
// Usage:
// if (DoesDatabaseExist("SQLServer", "MyDatabase", "SQL Server", "sa", "saPassword") = FALSE) then
//
///////////////////////////////////////////////////////////////////////////////
function BOOL DoesDatabaseExist(svServerName, svDatabaseName, svDriver, svUserName, svUserPassword)
OBJECT pADOConnObj, pADORecordSetObj;
STRING szADOConnObjID, szADORecordSetObjID, szConnString, szSQL;
BOOL bExists;
begin
try
bExists = FALSE;

// Create ADO Connection Object to connect to the SQL server
szADOConnObjID = "ADODB.Connection";
set pADOConnObj = CreateObject(szADOConnObjID);

// Create the SQL string to complete the connection
svDriver = "SQL Server";

if(gbWinAuthLogin == FALSE) then
szConnString = "driver={" + svDriver + "};";
szConnString = szConnString + "server=" + svServerName + ";";
szConnString = szConnString + "uid=" + svUserName + ";";
szConnString = szConnString + "pwd=" + svUserPassword + ";";
szConnString = szConnString + "database=master";
else
szConnString = "driver={" + svDriver + "};";
szConnString = szConnString + "server=" + svServerName + ";";
szConnString = szConnString + "database=master;";
szConnString = szConnString + "Trusted_connection=Yes";
//ConnectionString = "Data Source="+gsDBServer+";Initial Catalog="+gsDBCatalog+"; Integrated Security=True;";

endif;
// Open the ADO Connection
pADOConnObj.Open(szConnString);

// Create ADO Recordset object for the return
szADORecordSetObjID = "ADODB.Recordset";
set pADORecordSetObj = CreateObject(szADORecordSetObjID);

// Set some ADO Recordset properties
pADORecordSetObj.CursorType = 3;
pADORecordSetObj.ActiveConnection = pADOConnObj;

// Create the SQL string to retrieve the database if it exists
szSQL = "Select name from sysdatabases where name='" + svDatabaseName + "'";

// Use the recordset to see if the database exists
pADORecordSetObj.Open(szSQL);
if (pADORecordSetObj.RecordCount = 1) then
bExists = TRUE;
endif;

return bExists;
catch
MessageBoxEx(Err.Description + ":Error occured","",SEVERE);
endcatch;
end;

Tuesday, September 14, 2010

Creating an Uninstall Shortcut for an InstallScript MSI Project

Hi All, Today I need to share how we can create shortcuts in our installshield projects for uninstalling existing installed product. When we use the below information, we were able to install a shortcut along with our application. This shortcut will trigger the uninstallation process.

A Basic MSI installation package can be uninstalled using this command line:
[SystemFolder]Msiexec.exe /x{PRODUCT_CODE}

However, using the same command line for an InstallScript MSI will not uninstall some registry entries, nor would the entry in the Add/Remove Programs applet be removed. This article explains the proper command line to execute the uninstall for an InstallScript MSI project via a shortcut.



Discussion:
InstallShield 12 and newer
Uninstall shortcuts for InstallScript MSI projects are launched through a cached version of the setup.exe. This file is cached in the InstallShield Installation Information folder when the installation has completed. Below are the steps to add a shortcut to launch the cached setup.exe for an uninstall shortcut:


1. Browse to the Shortcuts view or the Shortcuts view of a component.
2. Right-click the desired folder location for the shortcut and select New Shortcut to Preexisting File.
3. In the Browse for Shortcut Target dialog, select the [ProgramFilesFolder] folder. Click OK. Name the shortcut created.
4. Fill in the Target field for the new shortcut as follows:
[ProgramFilesFolder]InstallShield Installation Information\[ProductCode]\setup.exe
Note: Quotes are not required for the shortcut target, they will automatically be placed around the filename and path by Windows Installer at runtime. [ProductCode] will also be replaced at runtime with the Product Code of the MSI package being installed.
5. Fill in the Arguments field for the shortcut as follows:
/runfromtemp /x

For InstallShield 11.5 and earlier
When adding an uninstallation shortcut for an InstallScript MSI project, the file that would be launched is IDriver.exe rather than Msiexec.exe. IDriver.exe is installed automatically, and there is no need to manually install this file. Below are the steps to add a shortcut to launch IDriver and uninstall the InstallScript MSI project:


1. Browse to the Shortcuts view or the Shortcuts view of a component.
2. Right-click and select New Shortcut to Preexisting File.
3. In the Browse for Shortcut Target dialog, the directories that will point to 
    Idriver.exe should be made. The subfolders InstallShield\Driver\ x \Intel 32
    should be added under the [CommonFilesFolder]. The x is where the
    appropriate version of IDriver.exe should be indicated; that being a 7 or 8
    for Developer 7 or 8 projects respectively, or 9 or 10 for DevStudio 9 or X
    projects respectively.

4. Fill in the Arguments field as follows:
    
Arguments: /M{Product Code}
    
{Product Code} would be replaced with your actual product code. It might
    look similar to the following:

    Arguments: /M{7C071035-F334-11D5-818A-00C04F288311}


To find the Product Code for a particular project, follow these steps:
1. Open project in the InstallShield IDE.
2. Select the General Information view.
3. Select Product Properties.
4. Note the value of the Product Code field in the right panel

Friday, August 6, 2010

Problem in changing Logon Credentials of service

Hi, often in our installer products we need to install windows service. Sometimes requirement is such that user need to give input to the installer that whether he needs to install the services on behalf of 'Local Account' or custom user account.

There is no issue till we use 'Local Account' for running the services. But the moment we go for custom account, issues start coming on different machines regarding login credentials failure for the installed services.

For this scenario, there is an easy way-out. :)

Please note that service user we are going to use for services should have Log on as a service right. To perform these, there are some simple steps to follow:


Following are the steps to check
1. Start - Run - secpol.msc
2. Local Policies - User Rights Assignment - Log on as a service


Add User to it. And have fun with your services. :)

Issue in browsing User in SdLogonUser dialog

Hi, sometimes I faced issues in using inbuilt dialog "SdLogonUser" functionality in Installshield. Issues were like on clicking the Browse button for user, nothing happens. I was not able to browse the user. 
One of my senior sorted out this issue by doing a lot of R&D on web. Atlast we got some solution which is stated below:


Check if Computer Browsing service is enabled.
*****************************************************
TroubleShooting:
Method 1: Enable NetBIOS over TCP/IP and start the Computer Browser service
To resolve this issue, make sure that NetBIOS over TCP/IP is turned on and that the Computer Browser service is running on each computer in the workgroup. To do this, follow these steps.
Step 1: Enable NetBIOS over TCP/IP


1. Click Start, click Control Panel, and then click Network and Internet Connections.
2. Click Network Connections.
3. Right-click Local Area Connection, and then click Properties.
4. Click Internet Protocol (TCP/IP), and then click Properties.
5. Click the General tab, and then click Advanced.
6. Click the WINS tab.
7. Under NetBIOS setting, click Enable NetBIOS over TCP/IP, and then click OK two times.
8. Click Close to close the Local Area Connection Properties dialog box.
9. Close the Network Connections window.


Step 2: Start the Computer Browser service


1. Click Start, right-click My Computer, and then click Manage.
2. Double-click Services and Applications.
3. Double-click Services.
4. On the right side, right-click Computer Browser, and then click Start.
5. Close the Computer Management window.


Back to the top:
Method 2: Install File and Print Sharing and make sure that it is not blocked by Windows Firewall
Step 1: Install File and Print Sharing for Microsoft Networks


1. Click Start, click Run, type ncpa.cpl, and then click OK.
2. Right-click Local Area Connection, and then click Properties.
3. Click the General tab, and then click Install.
4. Click Service, and then click Add.
5. In the Network Service list, click File and Print Sharing for Microsoft Networks, and then click OK.
6. Click Close.


Step 2: Make sure that File and Printer Sharing is not blocked by Windows Firewall


1. Click Start, click Run, type firewall.cpl, and then click OK.
2. On the General tab, make sure that the Don't allow exceptions check box is not selected.
3. Click the Exceptions tab.
4. On the Exceptions tab, make sure that the File and Printer Sharing check box is selected, and then click OK.

Monday, July 19, 2010

Fully qualified path about Setup.exe in InstallScript MSI project

Sometimes we need to get the value of the directory from where we are running the setup.exe. 


For ex:
To execute some scripts placed on the CD-ROM along with the exe or msi.



GetCurrentDir()


Syntax:
GetCurrentDir( svCurrentDir );
where svCurrentDir returns the current directory.

Thursday, July 8, 2010

Check Windows installer version

The set keyword must precede the assignment of an OBJECT variable to a reference returned by the CreateObject function. For example:

function OnBegin( )
OBJECT oMSI;
begin
// create the object
set oMSI = CreateObject("WindowsInstaller.Installer");

// use the object (display MSI version on user's system)
MessageBox("Your MSI version is: " + oMSI.Version, INFORMATION);

// free the object
set oMSI = NOTHING;
end;

Wednesday, July 7, 2010

To get TextChange, ListBox select, ComboBox Select event


CtrlGetSubCommand:
The CtrlGetSubCommand function retrieves the action performed on a control in a custom dialog. For example, CtrlGetSubCommand can tell you if the user single-clicked or double-clicked a list box or combo box control. It can also tell you when the contents of an edit field have changed.

Advanced developers can call CmdGetHwndDlg to handle additional information.

Syntax:

CtrlGetSubCommand (szDialogName);

szDialogName -- Specifies the name of a custom dialog.
Return Values:
CtrlGetSubCommand Return Values:
EDITBOX_CHANGE (-1007) -- The contents of the edit box have changed.

LISTBOX_ENTER (-1008) -- The user double-clicked a list box item.
LISTBOX_SELECT (-1009) -- The user single-clicked a list box item.

Monday, July 5, 2010

Working with XML

Hi, following post will give you a clear idea how to update xml element data, with the help of installshield data. This scenario will come where we need to either modify config files at the end of our installation program.


// Set XML Doc Type
set oDoc = CreateObject("Msxml2.DOMDocument.4.0");
oDoc.setProperty("SelectionLanguage", "XPath");


// set up variable with fullpath to config file
szConfigFile = "C:\app.config" ;


// set up the XPath for your target node and setting you are going to change
szXPath = "/configuration/userSettings/Client.Properties.Settings/setting[@name='WebUrl']";


// load the XML document into memory
if oDoc.load(szConfigFile) then


//Set the XPath in the XML
set oNode = oDoc.documentElement.selectSingleNode(szXPath);


//Update the XML Element
oNode.text = szCurrentURI;


endif;


// Save the XML Changes
oDoc.save(szConfigFile);


set oDoc = NOTHING;

Tuesday, June 29, 2010

Set character limit on Text Box

Hi friends,
This post will help you set characters limit on your text box control on your custom dialogs as well as inbuilt dialogs. Often we need to set limit on such type of controls like in case of password controls. Please use the code below and modify according to you if needed.

hEditName = CtrlGetDlgItem("", hDlg, SD_EDIT_NAME);
where,
hEditName -- Handler to the text box control.
hDlg -- handler to the dialog
SD_EDIT_NAME -- name of the text control here. You can change this parameter by the name of your control.

if (nLen < 255) then
nLen = 255;
endif;
SendMessage(hEditName, EM_LIMITTEXT, nLen-1, 0);

Friday, June 25, 2010

Updated Custom file Browse Dialog

Hello friends, following post will be very helpful to you in the scenarios where you need to provide a custom file browse control to the user. Installshield do not provide any control which can provide such type of customization as you will find below.
Don't get confused with a lot of code below. That is so simple, that following my tips, even a novice can use it successfully.

1) The below code which I had marked with 'Code for BRWSDLG_H file', please copy this code in a new script file named 'BRWSDLG_H ', which you can create in your installshield 'Installscript' view.


Code for BRWSDLG_H file
------------------------------------
// Avoid multiple include collisions.
#ifndef _BRWSDLG_H_
#define _BRWSDLG_H_


// Options for Flags member of OPENFILENAME.
#define OFN_READONLY 0x00000001
#define OFN_OVERWRITEPROMPT 0x00000002
#define OFN_HIDEREADONLY 0x00000004
#define OFN_NOCHANGEDIR 0x00000008
#define OFN_SHOWHELP 0x00000010
#define OFN_ENABLEHOOK 0x00000020
#define OFN_ENABLETEMPLATE 0x00000040
#define OFN_ENABLETEMPLATEHANDLE 0x00000080
#define OFN_NOVALIDATE 0x00000100
#define OFN_ALLOWMULTISELECT 0x00000200
#define OFN_EXTENSIONDIFFERENT 0x00000400
#define OFN_PATHMUSTEXIST 0x00000800
#define OFN_FILEMUSTEXIST 0x00001000
#define OFN_CREATEPROMPT 0x00002000
#define OFN_SHAREAWARE 0x00004000
#define OFN_NOREADONLYRETURN 0x00008000
#define OFN_NOTESTFILECREATE 0x00010000
#define OFN_NONETWORKBUTTON 0x00020000
#define OFN_NOLONGNAMES 0x00040000
#define OFN_EXPLORER 0x00080000
#define OFN_NODEREFERENCELINKS 0x00100000
#define OFN_LONGNAMES 0x00200000


// CommDlgExtendedError-related defines.
#define CDERR_DIALOGFAILURE 0xFFFF
#define CDERR_DIALOGFAILURE_MSG "The file browse dialog box could not be created."
// The dialog box could not be created. The common dialog box function
// call to the DialogBox function failed. For example, this error occurs
// if the common dialog box call specifies an invalid window handle."
#define CDERR_FINDRESFAILURE 0x0006
#define CDERR_FINDRESFAILURE_MSG "The file browse dialog box function failed to find a specified resource."
// The common dialog box function failed to find a specified resource.
#define CDERR_INITIALIZATION 0x0002
#define CDERR_INITIALIZATION_MSG "The file browse dialog box function failed during initialization."
// The common dialog box function failed during initialization. This error
// often occurs when sufficient memory is not available.
#define CDERR_LOADRESFAILURE 0x0007
#define CDERR_LOADRESFAILURE_MSG "The file browse dialog box function failed to load a specified resource."
// The common dialog box function failed to load a specified resource.
#define CDERR_LOADSTRFAILURE 0x0005
#define CDERR_LOADSTRFAILURE_MSG "The file browse dialog box function failed to load a specified string."
// The common dialog box function failed to load a specified string.
#define CDERR_LOCKRESFAILURE 0x0008
#define CDERR_LOCKRESFAILURE_MSG "The file browse dialog box function failed to lock a specified resource."
// The common dialog box function failed to lock a specified resource.
#define CDERR_MEMALLOCFAILURE 0x0009
#define CDERR_MEMALLOCFAILURE_MSG "The common dialog box function was unable to allocate memory for internal structures."
// The common dialog box function was unable to allocate memory
// for internal structures.
#define CDERR_MEMLOCKFAILURE 0x000A
#define CDERR_MEMLOCKFAILURE_MSG "The common dialog box function was unable to lock the memory associated with a handle."
// The common dialog box function was unable to lock the memory associated
// with a handle.
#define CDERR_NOHINSTANCE 0x0004
#define CDERR_NOHINSTANCE_MSG "The ENABLETEMPLATE flag was set, but you failed to provide a corresponding instance handle."
// The ENABLETEMPLATE flag was set in the Flags member of the initialization
// structure for the corresponding common dialog box, but you failed to provide
// a corresponding instance handle.
#define CDERR_NOHOOK 0x000B
#define CDERR_NOHOOK_MSG "The ENABLEHOOK flag was set, but you failed to provide a pointer to a corresponding hook procedure."
// The ENABLEHOOK flag was set in the Flags member of the initialization
// structure for the corresponding common dialog box, but you failed to provide
// a pointer to a corresponding hook procedure.
#define CDERR_NOTEMPLATE 0x0003
#define CDERR_NOTEMPLATE_MSG "The ENABLETEMPLATE flag was set, but you failed to provide a corresponding template."
// The ENABLETEMPLATE flag was set in the Flags member of the initialization
// structure for the corresponding common dialog box, but you failed to provide
// a corresponding template.
#define CDERR_REGISTERMSGFAIL 0x000C
#define CDERR_REGISTERMSGFAIL_MSG "The RegisterWindowMessage function returned an error code when it was called by the file browse dialog box function. "
// The RegisterWindowMessage function returned an error code when it was called
// by the common dialog box function.
#define CDERR_STRUCTSIZE 0x0001
#define CDERR_STRUCTSIZE_MSG "The lStructSize member of the initialization structure for the corresponding file browse dialog box is invalid. "
// The lStructSize member of the initialization structure for the corresponding
// common dialog box is invalid.
#define FNERR_BUFFERTOOSMALL 0x3003
#define FNERR_BUFFERTOOSMALL_MSG "The buffer pointed to by the lpstrFile member of the OPENFILENAME structure is too small for the filename specified by the user."
// The buffer pointed to by the lpstrFile member of the OPENFILENAME structure
// is too small for the filename specified by the user. The first two bytes
// of the lpstrFile buffer contain an integer value specifying the size, in bytes
// (ANSI version) or 16-bit characters (Unicode version), required to receive the full name.
#define FNERR_INVALIDFILENAME 0x3002
#define FNERR_INVALIDFILENAME_MSG "A filename is invalid."
// A filename is invalid.
#define FNERR_SUBCLASSFAILURE 0x3001
#define FNERR_SUBCLASSFAILURE_MSG "An attempt to subclass a list box failed because sufficient memory was not available."
// An attempt to subclass a list box failed because sufficient memory was not available.




// OPENFILENAME structure. Notice that all string members below are declared
// as LONG. For example, lpstrFilter. Do not use STRING.
typedef OPENFILENAME
begin
LONG lStructSize;
HWND hwndOwner;
HWND hInstance;
POINTER lpstrFilter;
POINTER lpstrCustomFilter;
LONG nMaxCustFilter;
LONG nFilterIndex;
POINTER lpstrFile;
LONG nMaxFile;
POINTER lpstrFileTitle;
LONG nMaxFileTitle;
POINTER lpstrInitialDir;
POINTER lpstrTitle;
LONG Flags;
SHORT nFileOffset;
SHORT nFileExtension;
POINTER lpstrDefExt;
POINTER lCustData;
POINTER lpfnHook;
POINTER lpTemplateName;
end;


// Windows API declares.
prototype comdlg32.GetOpenFileNameA( LONG );
prototype comdlg32.CommDlgExtendedError();
prototype user32.wsprintf(BYREF STRING, BYREF STRING, POINTER);


// Our file browse API, defined in matching .rul file.
prototype FileBrowseDlg( BYREF STRING, STRING, STRING, STRING, BOOL, LIST , BOOL);


#endif


2) Now create a new .rul file named 'BRWSDLG_RUL '.  Copy the below code in that .rul file.
-------------------------------------------


Code for BRWSDLG_RUL file
------------------------------------


#ifndef _BRWSDLG_RUL_
#define _BRWSDLG_RUL_


prototype Kernel32.RtlMoveMemory(BYREF STRING, POINTER, NUMBER);


typedef STR260
begin
STRING sz[260];
end;


///////////////////////////////////////////////////////////////////////////////
//
// FileBrowseDlg() uses the Windows GetFileNameA function to allow single file
// selection. Callers specify filter, dialog title, and initial browse
// directory.
//
// Inputs:
//
// szFile: String variable into which FileBrowseDlg() will place the selected
// file's fully qualified pathname. The variable passed in as szFile can
// be dynamically sized. szFile is passed by reference.
//
// nszFileSize: The size that szFile was explicitly declared as.
//
// szFilter: Filter spec for dialog. In the form "|||". For example:
//
// "Text files (*.txt)|*.txt|All files (*.*)|*.*||"
//
// The description ("Text files (*.txt)" above) must be separated from
// the extension ("*.txt" above) by a pipe "|" character. The entire
// string must end in a double || ("||").
//
// szDialogTitle: A string containig the title to display on the file
// browse dialog.
//
// szInitialDir: A string specifying the directory the browse dialog
// initially opens to.
//
// bMultiSel: Set to TRUE if you wish to enable multiple selection.
//
// listFiles: List that will be loaded with directory and filenames if
// multiple selection is enabled (i.e. bMultiSel = TRUE).
// List is passed by reference (by default, since list variables
// are pointers).
//
// bDerefLinks: Set to TRUE if you want to dereference shell links (also
// known as shortcuts). If TRUE, then choosing a shell link causes
// it to be dereferenced by the shell.
//
// Returns:
//
// Returns 0 when a file is successfully selected. Returns less than
// zero when when the user cancels/closes the browse dialog or an
// error occurs. If an error occurs, a message box displays the error
// identifier.
//
// History:
//
// 03-12-99 RBS Updated this header to correctly document bDerefLinks.
// 06-01-01 RBS Merged in changes from latest on InstallShield's site.
//
///////////////////////////////////////////////////////////////////////////////

function FileBrowseDlg( szFile, szFilter, szDialogTitle, szInitialDir,
bMultiSel, listFiles, bDerefLinks)

OPENFILENAME ofn;
STRING szMsg, szFileTitle[260];
STRING szCustomFilter[260], szTemp[260];
LONG nLen, nCount, nResult, n, nFlags, nErr;
STR260 str;

begin

// Replace each '|' character in szFilter with '\0' since that is
// what is required by the Win32 API.
nLen = StrLength( szFilter );
nLen = nLen - 1;
nCount = 0;
for nCount = 0 to nLen
if ( szFilter[nCount] = '|' ) then
szFilter[nCount] = '\0';
endif;
endfor;

UseDLL(WINSYSDIR ^ "comdlg32.dll");

nFlags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
| OFN_NOCHANGEDIR | OFN_EXPLORER;
if bMultiSel then
nFlags = nFlags | OFN_ALLOWMULTISELECT;
endif;
if bDerefLinks = FALSE then
nFlags = nFlags | OFN_NODEREFERENCELINKS;
endif;

nResult = GetWindowHandle(HWND_INSTALL);

ofn.lStructSize = SizeOf(ofn);
ofn.hwndOwner = nResult;

// The string pointed to by ofn.lpstrFile is modified by
// GetOpenFileName. The only way to have a string in
// script to reflect the change is to point lpstrFile
// to a structure that contains just a string member.
str.sz = szFile;
ofn.lpstrFile = &str;
ofn.nMaxFile = SizeOf(str);

// Notice how the address of an explicitly sized string
// is used when assigning to a member who was declared
// as a LONG string pointer (lpstr). For example, &szFilter.
ofn.lpstrFilter = &szFilter;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = &szFileTitle;
ofn.nMaxFileTitle = 260;
ofn.lpstrTitle = &szDialogTitle;
ofn.Flags = nFlags;
ofn.lpstrDefExt = &szTemp;
ofn.lpstrInitialDir = &szInitialDir;
ofn.hInstance = 0;
ofn.lpstrCustomFilter = &szCustomFilter;
ofn.nMaxCustFilter = 260;
ofn.lpfnHook = 0;

nResult = GetOpenFileNameA(&ofn);
if nResult = 1 then
if bMultiSel then
// A direct assignment in the form of szFile = str.sz
// will result in all data beyond the first null to be
// lost. This only happend when the string is assigned
// with a structure member. This is the reason why a
// very indirect method is being used the transfer the
// contents of str.sz to szFile.
Resize( szFile, SizeOf(str));
RtlMoveMemory( szFile, &str, SizeOf(str));
StrGetTokens( listFiles, szFile, "");
else
szFile = str.sz;
endif;
else
// We had an error in GetOpenFileNameA() so get the error string.
nErr = CommDlgExtendedError();
switch (nErr)
case CDERR_DIALOGFAILURE: szMsg = CDERR_DIALOGFAILURE_MSG;
case CDERR_FINDRESFAILURE: szMsg = CDERR_FINDRESFAILURE_MSG;
case CDERR_INITIALIZATION: szMsg = CDERR_INITIALIZATION_MSG;
case CDERR_LOADRESFAILURE: szMsg = CDERR_LOADRESFAILURE_MSG;
case CDERR_LOADSTRFAILURE: szMsg = CDERR_LOADSTRFAILURE_MSG;
case CDERR_LOCKRESFAILURE: szMsg = CDERR_LOCKRESFAILURE_MSG;
case CDERR_MEMALLOCFAILURE: szMsg = CDERR_MEMALLOCFAILURE_MSG;
case CDERR_MEMLOCKFAILURE: szMsg = CDERR_MEMLOCKFAILURE_MSG;
case CDERR_NOHINSTANCE: szMsg = CDERR_NOHINSTANCE_MSG;
case CDERR_NOHOOK: szMsg = CDERR_NOHOOK_MSG;
case CDERR_NOTEMPLATE: szMsg = CDERR_NOTEMPLATE_MSG;
case CDERR_REGISTERMSGFAIL: szMsg = CDERR_REGISTERMSGFAIL_MSG;
case CDERR_STRUCTSIZE: szMsg = CDERR_STRUCTSIZE_MSG;
case FNERR_BUFFERTOOSMALL: szMsg = FNERR_BUFFERTOOSMALL_MSG;
case FNERR_INVALIDFILENAME: szMsg = FNERR_INVALIDFILENAME_MSG;
case FNERR_SUBCLASSFAILURE: szMsg = FNERR_SUBCLASSFAILURE_MSG;
endswitch;
if nErr != 0 then
// User did not close or cancel dialog box.
MessageBox("FileBrowseDlg() error:\n\n" + szMsg, SEVERE);
endif;
return -1;
endif;

UnUseDLL(WINSYSDIR ^ "comdlg32.dll");

return 0;
end;

#endif

3) Now place the below code in the default setup.rul file.
----------------------------------------
Code for SETUP.rul file
------------------------------------
// Need declarations for FileBrowseDlg and friends.
#include "brwsdlg.h"

// FileBrowseDlg requires the szFile parameter be explicitly sized
// and that the size be passed as the second parameter.
STRING szFile, svDir, svFileList, svTemp, szFilter;
NUMBER nResult, nReturn, nvLen, nCount;
BOOL bMultiSel, bDerefLinks;
LIST listFiles;

program

// If I want to support multiple selection, set bMultiSel to TRUE
// and pass in a valid string list.
bMultiSel = TRUE;
bDerefLinks = FALSE;
listFiles = ListCreate(STRINGLIST);
szFilter = "Text files (*.txt)|*.txt|All files (*.*)|*.*||";

// Open the file browse dialog.
nResult = FileBrowseDlg( szFile,
szFilter,
"Select the DCOM98.EXE File",
"c:\\",
bMultiSel,
listFiles,
bDerefLinks );
if nResult = 0 then
if bMultiSel then
// If I chose multiple selection, I must parse the info, which is stored
// in list. First item will be dir, all others are individual filenames.
nReturn = ListGetFirstString(listFiles, svTemp);
while nReturn != END_OF_LIST
svFileList = svFileList + svTemp + "\n";
nReturn = ListGetNextString(listFiles, svTemp);
endwhile;
MessageBox("Directory (first item) and selected files:\n\n" + svFileList, 0);
else
// No multiple selection, so a single file/path was set.
MessageBox("Selected file:\n\n" + szFile, 0);
endif;
endif;

ListDestroy(listFiles);
endprogram


#include "brwsdlg.rul"



3) You can modify the filters which you want by changing the value in variable szFilter in above code.


For any problem, you are free to write to me...

Thursday, June 24, 2010

Hiding a Control in a InstallScript Project Dialog

Hello Friends, following post will be helpful in following types of project:
InstallScript, InstallScript MSI
Summary
This article explains how a control can be hidden during installation run time.
Discussion:

1. Get the handle to the dialog. This is done using the CmdGethwndDlg function:
hwndDlg = CmdGetHwndDlg( szDlg );
where hwndDlg is a variable defined as a handle, and szDlg is the dialog on which the desired control appears.

2. Get the handle to the control that will be used later. This is done with the GetDlgItem command:
hCtrl = GetDlgItem(hwndDlg, SD_RADIO_COMPACT);


3. Use the function that hides the control. This is done using the ShowWindow function. The following is a sample:
nResult = ShowWindow(hCtrl, SW_HIDE);
In the function, the first parameter used is the handle to the dialog you received in Step 2. The second parameter is the constant that is defined for you that states to hide the control.

Friday, June 18, 2010

Accessing or Setting Windows Installer Properties Through Deferred, Commit, and Rollback Custom Actions

Project Type:
Basic MSI, InstallScript MSI



Hi Friends, often we need to use deferred type custom actions in your project. Many readers will wonder what the hell are these deffered custom actions. Don't worry my friends, I am here at your help.

For help about Deffered custom actions, please read my post named "Deffered Custom Actions".

In Deferred, Commit, and Rollback type custom actions, we are having access to only some of the in-built Windows Installer properties like:
CustomActionData, ProductCode, and UserSID.

If we want to access any other properties during the Deferred, Commit, and Rollback execution, then we need to pass those properties in CustomActionData.
 We need to schedule an immediate set-a-property type of custom action to set a property that matches the name of the custom action. The value of this property is then available in the CustomActionData property within the deferred, commit, or rollback custom action.

Using CustomActionData to Access a Property:
Following example will show you how to access the Windows Installer property SUPPORTDIR through a deferred InstallScript custom action.
  1. In the Custom Actions and Sequences view, create a set-a-property custom action (type 51) called GetSUPPORTDIR. Configure the Property Name, Property Value, and Install Exec Sequence settings for the custom action as follows, and leave all of the other settings blank.
    • Property Name: DisplaySupportDir
    • Property Value: [SUPPORTDIR]
    • Install Exec Sequence: After InstallInitialize
  2. In the InstallScript view, create a new function called DisplaySupportDir.
  3. Add the following code to display a message box containing the value of SUPPORTDIR:

    function DisplaySupportDir(hMSI)
       STRING supportDirPath;
       NUMBER supportDirPathBuffer;
    begin
       supportDirPathBuffer = MAX_PATH;
       if(MsiGetProperty(hMSI, "CustomActionData", supportDirPath, supportDirPathBuffer) == ERROR_SUCCESS) then
         SprintfBox(INFORMATION,"Deferred Execution","The value of SUPPORTDIR is %s",supportDirPath);
         SprintfBox(INFORMATION,"Deferred Execution","The value of InstallScript's SUPPORTDIR is %s",SUPPORTDIR);
       endif;
    end;
  4. In the Custom Actions and Sequences view, create an InstallScript custom action called DisplaySupportDir. Configure the Function Name, In-Script Execution, and Install Exec Sequence settings for the custom action as follows, and leave all of the other settings blank.
    • Function Name: DisplaySupportDir
    • In-Script Execution: Deferred Execution in System Context
    • Install Exec Sequence: After GetSUPPORTDIR

You could also use the below Installscript custom action which I had used in most of my projects for accessing multiple properties set in CustomActionData.

For example, if you want to retrieve the values of [INSTALLDIR], [SUPPORTDIR], and [SetupType], you would set the Property Value setting of your type 51 custom action to this:

[INSTALLDIR];[SUPPORTDIR];[SetupType]

where each property is separated by a semicolon.
----------------------------------------------------------------------------------------------
STRING sPropArray(2);

MsiGetProperty(hMSI, "CustomActionData", svProp, nLength);


listID = ListCreate (STRINGLIST);
if (StrGetTokens (listID, svProp, ";") > 0) then
//MessageBox ("StrGetTokens failed.", SEVERE);
else
nIndex=0;
// Get the first number from the list.
nResult = ListGetFirstString(listID, svString);
while (nResult != END_OF_LIST)
NumToStr(sIndex,nIndex);
//MessageBox (sIndex, SEVERE);
sPropArray(nIndex)=svString;
nIndex=nIndex+1;
nResult = ListGetNextString (listID, svString);
endwhile;
endif;
ListDestroy (listID);
---------------------------------------------------------------------------------------------
http://kb.flexerasoftware.com/selfservice/viewContent.do?externalID=Q104413

Create a Shortcut on the Quick Launch Bar

At times, it may be necessary to create a shortcut for your application on the Quick Launch bar. This article discusses how to do so.

Discussion:
Windows allows users to place shortcuts on the Quick Launch bar on the bottom left side of the taskbar. By default, InstallShield does not provide a Quick Launch folder under the Shortcuts view to allow the creation of shortcuts on the target machine's Quick Launch bar. However, there is a way to do it manually. Once you create the folder, you can place your shortcut there. To configure the shortcut under the Shortcuts view, follow these instructions.
  1. Select the Shortcuts view under System Configuration.
  2. Select the Shortcuts node in the middle pane.
  3. Right-click and select the Show Folder option from the context menu.
  4. Select [AppDataFolder] from the list of predefined folders. This adds [AppDataFolder]directly under the Shortcuts node.
  5. Select [AppDataFolder].
  6. Right-click and select New Folder.
  7. Rename the folder to Microsoft.
  8. Continue to create the following folder structure under the Microsoft folder: \Internet Explorer\Quick Launch.Once you are done you should have the following folder structure:
    [AppDataFolder]\Microsoft\Internet Explorer\Quick Launch
  9. Create your application shortcut under the Quick Launch folder.

Get SUPPORTDIR path in Basic Msi Project

Hi friends, today I spent few moments in sorting out how to get the path of SUPPORTDIR in a basic MSI project. Although working in Installscript suits me much, its convenient also. But how it can be achieved via custom actions seems to be tedious at first glance.
But at last, finds way to the road much wider. :)

You need to declare the prototype for the below stated function.

export prototype STRING GetSupportFilePathMSI(HWND);

function STRING GetSupportFilePathMSI(hMSI)
     STRING supportDirPath;
     NUMBER nLength;
begin
     nLength = 256;
     // Returns path to the Support files path extracted on the target machine.
     MsiGetProperty(hMSI, "SUPPORTDIR", supportDirPath, nLength);
     return supportDirPath;
end;

The function returns the path of the SUPPORTDIR, which you needs to save in a variable.

CheckValidServerURLPath

//***************************************************************************
//FUNCTION NAME : CheckValidServerURLPath
//Purpose : Check the syntax of Transcend server url.
//***************************************************************************
function BOOL CheckValidServerURLPath(serverPath)
NUMBER nLength,nResult,nLocation;
STRING svString,szFindMe,szMsg;
BOOL bValid;
begin
nLength = StrLength (serverPath);
if (nLength = 0) then
MessageBoxEx("Please enter Transcend Server URL.","",INFORMATION);
return FALSE;
else
nLength=256;
bValid=FALSE;
// Check for presence of http://, https://, file:// in the URL
//********************START***************************
nResult = Is (VALID_PATH, serverPath);
if (nResult = TRUE) then
bValid=TRUE;
else
szMsg="The server URL you entered is invalid. Please try again";
MessageBoxEx(szMsg,"",WARNING);
return FALSE;
endif;
//********************END***************************

// Search for "/" at the end of the server URL and removing it, if exist.
//********************START***************************
if(StrRemoveLastSlash (serverPath) =0) then // removing trailing "\" at end
// Ckeck for trailing "/" at end
// START
nResult=StrFindEx ( serverPath, "/", nLength-1);
if(nResult>=0) then
StrSub(serverPath,serverPath,0,nLength-1);

if(MsiSetProperty(ISMSI_HANDLE,"CUSTOMURL",serverPath)==ERROR_SUCCESS) then
endif;
endif;
// END
endif;
//********************END***************************
return TRUE;
endif;
end;

Check Multiple instance of setup to run at one time

Hello Friends, today I want to share a script which will help you in preventing multiple instances of an installer simultaneously. For this you need to declare below two prototypes in your setup.rul file. 


      prototype Kernel32.CreateMutex(byval NUMBER, BOOL, STRING);
      prototype Kernel32.ReleaseMutex(HWND);


Below I had used a function named 'CheckMultipleInstance' which will help in preventing multiple instances of an installshield installer to run at a time.


//***************************************************************************
//FUNCTION NAME : CheckMultipleInstance
//Purpose : To prevent multiple instances of setup to run at one time.
//***************************************************************************
function CheckMultipleInstance(hMSI)
     STRING strMutex;
     HWND hMutex;
     NUMBER nError;
begin
     strMutex = "MY_SETUPMUTEX" ;
     hMutex = Kernel32.CreateMutex(NULL, TRUE, strMutex);
     nError = Err.LastDllError();
     if (hMutex != 0 && nError == 183) then
            MessageBoxEx("Another instance of this setup is already
            running","",SEVERE);

            ReleaseMutex(hMutex);


            if(MsiSetProperty(ISMSI_HANDLE,"ANOTHERINSTANCE","1") ==
                ERROR_SUCCESS) then

                SprintfMsiLog("CUSTOM LOG: %s","Version is not Valid.");
            endif;


            abort;
      else
            if(MsiSetProperty(ISMSI_HANDLE,"ANOTHERINSTANCE","0") ==
                ERROR_SUCCESS) then

                SprintfMsiLog("CUSTOM LOG: %s","Version is not Valid.");
            endif;
      endif; 
end;

Check Free space on disk

function CheckFreeSpace(hMSI)
_DISK_INFO di;
NUMBER n; // to do: something with function return values
NUMBER nvSizeTargetHigh, nvSizeTargetLow, nUnitsTarget;
begin
// init. _DISK_INFO members: what drive, what info
di.szDiskPath = INSTALLDIR;
di.nInfoToQuery = DISK_INFO_QUERY_DISK_FREE_SPACE;

n = GetDiskInfo(&di);
// change to desired unit for free space
nUnitsTarget = MBYTES;

n = ConvertSizeToUnits(di.nFreeSpaceHigh, di.nFreeSpaceLow, BYTES,nvSizeTargetHigh, nvSizeTargetLow, nUnitsTarget);

//SprintfBox(INFORMATION, "Free Space", "Free space: %d MB", nvSizeTargetLow);
if(nvSizeTargetLow<500) then
MsiSetProperty(hMSI,"VALIDFREESPACE","0");
MessageBoxEx("Atleast 500 MB space is required on the destination drive to install Beyond TXT G4 Client. Please free the required space and try again..","",INFORMATION);
else
MsiSetProperty(hMSI,"VALIDFREESPACE","1");
endif;
end;

Monday, May 24, 2010

Calulate Disk Space

Hi, often we need in our installer projects to calculate the disk space on the target machine.

Below code can be edited for this purpose:


function DiskSpace(hMSI)
    NUMBER n;
    STRING szAvailableSpace;
    NUMBER nUnitUsed,nNum,nLocation;
    BOOL bReq;
    STRING sLoc;
begin
     nUnitUsed = GBYTES;
     ISRT._GetDiskSpaceExEx(INSTALLDIR,szAvailableSpace,nUnitUsed,TRUE,TRUE);

     StrToNum(nNum,szAvailableSpace);
     if(nNum<1) then
          nUnitUsed=MBYTES;  
          ISRT._GetDiskSpaceExEx(INSTALLDIR,szAvailableSpace,nUnitUsed,TRUE,TRUE);  
          StrToNum(nNum,szAvailableSpace);
       
         if(nNum<1024) then
              nUnitUsed=KBYTES;
              ISRT._GetDiskSpaceExEx(INSTALLDIR,szAvailableSpace,nUnitUsed,TRUE,TRUE);
              StrToNum(nNum,szAvailableSpace);
     
              if(nNum<1048576) then
                   nUnitUsed=BYTES;
                   ISRT._GetDiskSpaceExEx(INSTALLDIR,szAvailableSpace,nUnitUsed,TRUE,TRUE);
                   szAvailableSpace = szAvailableSpace + " " + StrConvertSizeUnit(BYTES ); //
                   MessageBox("Available space--> "+szAvailableSpace,TRUE);
                   MsiSetProperty(hMSI,"DISKSPACE",szAvailableSpace);
             endif;
             szAvailableSpace = szAvailableSpace + " " + StrConvertSizeUnit( KBYTES );
             MsiSetProperty(hMSI,"DISKSPACE",szAvailableSpace);
         else
             szAvailableSpace = szAvailableSpace + " " + StrConvertSizeUnit( MBYTES );
             MsiSetProperty(hMSI,"DISKSPACE",szAvailableSpace);
         endif;
    endif;

//////////////
nLocation = StrFind (szAvailableSpace, ".");
NumToStr(sLoc,nLocation);
StrSub(szAvailableSpace,szAvailableSpace,0,2);


szAvailableSpace = szAvailableSpace + " " + StrConvertSizeUnit( GBYTES );
//MessageBox("Available space--> "+szAvailableSpace,TRUE);
MsiSetProperty(hMSI,"DISKSPACE",szAvailableSpace);
end;