Monday, January 24, 2011

Read Write XML data

In Installshield projects, often we need to manipulate XML, either by reading specific values, or updating some values. For e.g. in case of configs. The examples mentioned below will be very handy when dealing with XML's.
Reading XML data:
// Creating an object for MsXML2 for xml manipulation
set oDoc = CoCreateObject("Msxml2.DOMDocument");
oDoc.setProperty("SelectionLanguage", "XPath");


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


// Set XPath in XMl file to work on...
szXPath = "/configurations/setting[@name='AuthenticationAuthService']";


// This will return the default value for AuthenticationAuthService
sAuthServerURL = ReadDefaultConfigValues(szXPath,"default",oDoc);


Function: The below function is a custom action which will return the values from the XML tag elements.


function STRING ReadDefaultConfigValues(szXPath,szkey,oDoc)
OBJECT oNode;
begin
set oNode = oDoc.documentElement.selectSingleNode(szXPath);
if(gbConfigImported==FALSE) then
return oNode.attributes.getNamedItem(szkey).value;
else
return oNode.text;
endif;
end;
-------------------------------
Writing/saving XML data
set oDoc = CoCreateObject("Msxml2.DOMDocument");
oDoc.setProperty("SelectionLanguage", "XPath");


// set up variable with fullpath to xml config file
szConfigFile = gWIN_INSTALLDIR^"PaginationService\\TranscendBTG4.PaginationService.exe.config";


Configure_ConfigFile(szConfigFile,"/configuration/connectionStrings/add[@name='TranscendConnectionString']",strConnectionString,"connectionString",oDoc);


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


Function: The below function is a custom action which will save/update the values from the XML tag elements.


function Configure_ConfigFile(szConfigFile,szXPath,strValue,key,oDoc)
OBJECT oNode;
begin
// load the XML document into memory
if oDoc.load(szConfigFile) then
//Set the XPath in the XML
set oNode = oDoc.documentElement.selectSingleNode(szXPath);
oNode.attributes.getNamedItem(key).value = strValue;
endif;
end;

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;