Video Screencast Help
Search Video Help Close Back
to help
New in the Rewards Catalog: Vouchers for "Symantec Technical Specialist" and "Symantec Certified Specialist" exams.

Another error 1721 question

Updated: 21 May 2010 | 14 comments
Alan Sinclair 2's picture
0 0 Votes
Login to vote

Here's another situation where the WiseDll.DLL is getting lost, but it's a bit different than Question about error 1721 so I'm hoping someone might have cleverer ideas than me.



Here's the error from the log:

Action start 15:34:59: DN_WS_CheckFileVersReboot.F24CDE6F_EC9B_46B0_90B4_4C3A38E8D18E.

MSI (s) (44:68) [15:34:59:313]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSIF.tmp, Entrypoint: g0

Info 2835.The control ErrorIcon was not found on dialog SetupError.

Error 1721.There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: , location: WiseCustomCall, command: g0



The MSI in question installs just fine on a clean system -- the failure only occurs when the MSI is being installed as an upgrade to an earlier version.



The reply to the question above points to KB Article 1723 but in this case the WiseDll.DLL (version 7.3.0.250 - the same version as in my WIS 7) is correctly in the package, and in fact IS PRESENT as C:\Windows\Installer\MSIF.tmp when the error dialog is displayed. (And looks ok internally, using Depends.exe!)



There's a possible wrinkle in that the failing custom action is from my WIS-created merge module, but the final MSI has been made by (cough, not me) InstallShield. I don't see why that would make any difference though -- I can image IS stripping out WIS dlls, but the dll isn't missing.



Is this something that Wise should be asked (paid) look into?



I guess the alternative would be to write our own DLL, instead of using WiseScript.



ANY hints, clues or suggestions will be much appreciated.

discussion Filed Under:

Comments

Bill MacEachern's picture
03
Mar
2009
0 Votes 0
Login to vote

What is the CA supposed to be doing when it fails? Any hits in filemon or regmon when it fails?

EdT's picture
03
Mar
2009
0 Votes 0
Login to vote

Also, which operating system are you testing on?

If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.

Alan Sinclair 2's picture
03
Mar
2009
0 Votes 0
Login to vote

The CA is supposed to run a bit of WiseScript that compares the version of an installed file with the version of a file in the package (to determine if a reboot will be needed.) It's between InstallInitialize and AllocateRegistrySPace, as immediate CA type 1.



I haven't looked at reg or file hits -- maybe I'd better go do that.





Fwiw here's the WiseScript



Get Windows Installer Property DN_INSTALLED_FILE_NAME into IFileName

Get System Information into IFILEVER

Set Windows Installer Property DN_INSTALLED_FILE_VERSION to %IFILEVER%

Get Windows Installer Property DN_PACKAGE_FILE_VER into MFILEVER

Rem split up versions into MAX, MIN and REV, dropping BLD

Parse String "%IFILEVER%" into IVERMAX and IVERREST

Parse String "%IVERREST%" into IVERMIN and IVERREST

Parse String "%IVERREST%" into IVERREV and IVERBLD

Parse String "%MFILEVER%" into MVERMAX and MVERREST

Parse String "%MVERREST%" into MVERMIN and MVERREST

Parse String "%MVERREST%" into MVERREV and MVERBLD

Rem show inst and pkg versions: maxs, mins & revs

Rem if installed ver is HIGHER than pkg ver then msiexec will not overwrite it

Rem and if equal then no problem either

Rem so reboot only rqd if iver is LESS than mver

If IVERMAX Less Than "%MVERMAX%" then

Display Message "DNWS IMax LT MMax"

Set Windows Installer Property DN_FILEVER_MISMATCH to 1

ElseIf IVERMAX Equals "%MVERMAX%" then

If IVERMIN Less Than "%MVERMIN%" then

Set Windows Installer Property DN_FILEVER_MISMATCH to 1

ElseIf IVERMIN Equals "%MVERMIN%" then

If IVERREV Less Than "%MVERREV%" then

Set Windows Installer Property DN_FILEVER_MISMATCH to 1

End

End

End

Rem DN_FILEVER_MISMATCH is used as condition for VBS CA setting RebootAtEnd

Alan Sinclair 2's picture
03
Mar
2009
0 Votes 0
Login to vote

Tested on XP and on WinServer 2003 -- happens on both.

EdT's picture
03
Mar
2009
0 Votes 0
Login to vote

Is the CA running in system context or user context?

If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.

Alan Sinclair 2's picture
03
Mar
2009
0 Votes 0
Login to vote

User context (in the CA table, Type = 1) (I think immediate CAs can only be run as user?)

EdT's picture
03
Mar
2009
0 Votes 0
Login to vote

Can you try it in deferred, as a system context CA ?

If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.

Alan Sinclair 2's picture
04
Mar
2009
0 Votes 0
Login to vote

quote:
Originally posted by: EdT

Can you try it in deferred, as a system context CA ?




Run deferred in system context (type 3073) it fails the same way :-(



(Slow response from me due to sev 1 BSOD on my plate today)

Alan Sinclair 2's picture
04
Mar
2009
0 Votes 0
Login to vote

quote:
Originally posted by: Bill MacEachern

What is the CA supposed to be doing when it fails? Any hits in filemon or regmon when it fails?


The wisescript gets the version of an existing file on the target machine, gets a couple of properties from the MSI, compares values, and sets a property in the MSI (hence is run immediate, for access to properties.)



I couldn't spot anything in the regmon/filemon logs that looked wrong. The logs are huge, though, so I might have missed something -- is there anything you'd suggest to keep an eye for? The write of the DLL as a tmp file was OK.



AngelD's picture
04
Mar
2009
0 Votes 0
Login to vote

Use Process Monitor (ProcMon) instead.



Action start 15:34:59: DN_WS_CheckFileVersReboot.F24CDE6F_EC9B_46B0_90B4_4C3A38E8D18E.

MSI (s) (44:68) [15:34:59:313]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSIF.tmp, Entrypoint: g0



After "DN_WS_CheckFileVersReboot.F24CDE6F_EC9B_46B0_90B4_4C3A38E8D18E" action has failed do a search for (in the above case) "C:\WINDOWS\Installer\MSIF.tmp" in ProcMon. There will be a lot of writefile operations as the custom action binary is first extracted and when you find the load image operation the CA binary (MSIF.tmp) performs its "magic".



You could then filter on either the TID (thread id) or something else that will minimize the ProcMon output.



Alan Sinclair 2's picture
13
Mar
2009
0 Votes 0
Login to vote

For the record I gave up on using WiseScript for this and wrote a C++ dll to do the file version check instead. That's working for the customer now.



Thanks all for the help.

EdT's picture
15
Mar
2009
0 Votes 0
Login to vote

Any chance of a peek at your C++ source code?

If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.

Alan Sinclair 2's picture
16
Mar
2009
0 Votes 0
Login to vote

quote:
Originally posted by: EdT

Any chance of a peek at your C++ source code?




OK. (There may be a better way to show code in the forum but I've put it in quotes so the html doesn't break the post.) It's embarrassing to expose my coding but here's the relevant part:




quote:


//===============================================

//

// filevers.cpp

//

//===============================================



#include "stdafx.h"

#include "winver.h"



extern MSIHANDLE g_hSession; // global copy of handle, used for log file writes etc

extern DNLOG *DnLog; // global log file stuff



//-----------------------------------------------------------------------------

//

// DN_CheckFileVers

//

// Immediate Custom Action

// compare the installed version of an installed binary file with the version in the MSI

// if the MSI version is higher then set DN_FILEVER_MISMATCH (to trigger a reboot or whatever)

//

// this uses hardcoded names of properties in the MSI:

//

// DN_FILEVER_MISMATCH

// DN_PACKAGE_FILE_VER

// DN_INSTALLED_FILE_NAME

//

//-----------------------------------------------------------------------------



int _stdcall DN_CheckFileVers(MSIHANDLE a_hSession)

{

g_hSession = a_hSession; // save a global copy of the handle for messages etc

CString strFunc = TEXT("DN_CheckFileVers");

DnecaLogInit(THISDLLNAME);



CString strMsg;



try

{

CString strMsiVer = MGetProperty( a_hSession, TEXT("DN_PACKAGE_FILE_VER") );

CString strFileName = MGetProperty( a_hSession, TEXT("DN_INSTALLED_FILE_NAME") );

DnLogMessage( DnLog, SEV_INFO, TEXT("%s DN_PACKAGE_FILE_VER=%s DN_INSTALLED_FILE_NAME=%s"),

strFunc, strMsiVer, strFileName );



// although this CA is conditioned on the installed file being found by AppSearch

// only proceed if we got a filename



if(( 0 == strFileName.CompareNoCase("dnblank"))

|| ( strFileName.IsEmpty() ))

{

DnLogMessage( DnLog, SEV_INFO, TEXT("%s no action. Exiting because DN_INSTALLED_FILE_NAME (%s) is no good"),

strFunc, strFileName );

}

else

{

// filename is good, continue



// get version values for installed file

DWORD dwDummy;

DWORD dwFVISize = GetFileVersionInfoSize( strFileName , &dwDummy );

if( 0 == dwFVISize )

{

// identify problem

DWORD dwErr = GetLastError();

CString strErr;

DWORD dwMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,

NULL, dwErr, NULL, strErr.GetBuffer(MAX_PATH), MAX_PATH, NULL);

strErr.ReleaseBuffer( dwMsgLen );

strErr.TrimRight();

if( !dwMsgLen )

strErr.Format(TEXT("Unidentified system error"));

//MessageBox(GetForegroundWindow(), "InfoSize failed", "dneca", 0);

DnLogMessage( DnLog, SEV_INFO, TEXT("%s GetFileVersionInfoSize error, rc=%#x, err:%s"), strFunc, dwErr, strErr);

MSetProperty( a_hSession, TEXT("DN_FILEVER_MISMATCH"), TEXT("1") );

DnLogMessage( DnLog, SEV_INFO, TEXT("%s has set DN_FILEVER_MISMATCH = 1, just in case"), strFunc);

}

else

{

LPBYTE lpVersionInfo = new BYTE[dwFVISize];

if (!GetFileVersionInfo( strFileName , 0 , dwFVISize , lpVersionInfo ))

{

DWORD dwErr = GetLastError();

CString strErr;

DWORD dwMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,

NULL, dwErr, NULL, strErr.GetBuffer(MAX_PATH), MAX_PATH, NULL);

strErr.ReleaseBuffer( dwMsgLen );

strErr.TrimRight();

if( !dwMsgLen )

strErr.Format(TEXT("Unidentified system error"));

DnLogMessage( DnLog, SEV_INFO, TEXT("%s GetFileVersionInfo error, rc=%#x, err:%s"), strFunc, dwErr, strErr);

//MessageBox(GetForegroundWindow(), "GetInfo failed", "dneca", 0);

MSetProperty( a_hSession, TEXT("DN_FILEVER_MISMATCH"), TEXT("1") );

DnLogMessage( DnLog, SEV_INFO, TEXT("%s has set DN_FILEVER_MISMATCH = 1, just in case"), strFunc);

}

else

{

UINT uLen;

VS_FIXEDFILEINFO *lpFfi;

VerQueryValue( lpVersionInfo , _T("\\") , (LPVOID *)&lpFfi , &uLen );

DWORD dwFileVersionMS = lpFfi->dwFileVersionMS;

DWORD dwFileVersionLS = lpFfi->dwFileVersionLS;

delete [] lpVersionInfo;

DWORD dwFileMaj = HIWORD(dwFileVersionMS);

DWORD dwFileMin = LOWORD(dwFileVersionMS);

DWORD dwFileRev = HIWORD(dwFileVersionLS);

DWORD dwFileBld = LOWORD(dwFileVersionLS);

strMsg.Format("%s File Version: %d.%d.%d.%d",

strFunc, dwFileMaj, dwFileMin, dwFileRev, dwFileBld );

DnLogMessage( DnLog, SEV_INFO, strMsg );





// split the version data from the MSI package

// into Major, Minor, Revision & Build parts as dwords

CString strMsiMaj = "0";

CString strMsiMin = "0";

CString strMsiRev = "0";

CString strMsiBld = "0";

int iLen = 0;

int iP = 0;

iLen = strMsiVer.GetLength();

if(iLen > 0)

{

iP = strMsiVer.Find(_T("."));

strMsiMaj = strMsiVer.Left(iP++);

iLen = iLen - iP;

strMsiVer = strMsiVer.Right(iLen); // strMsiVer is consumed

}

iLen = strMsiVer.GetLength();

if(iLen > 0)

{

iP = strMsiVer.Find(_T("."));

strMsiMin = strMsiVer.Left(iP++);

iLen = iLen - iP;

strMsiVer = strMsiVer.Right(iLen);

}



iLen = strMsiVer.GetLength();

if(iLen > 0)

{

iP = strMsiVer.Find(_T("."));

strMsiRev = strMsiVer.Left(iP++);

iLen = iLen - iP;

if(iLen > 0)

{

strMsiBld = strMsiVer.Right(iLen);

}

}

DWORD dwMsiMaj = _ttoi((LPCTSTR) strMsiMaj);

DWORD dwMsiMin = _ttoi((LPCTSTR)strMsiMin);

DWORD dwMsiRev = _ttoi((LPCTSTR)strMsiRev);

DWORD dwMsiBld = _ttoi((LPCTSTR)strMsiBld);



strMsg.Format("%s DN_PACKAGE_FILE_VER strings %s-%s-%s-%s dwords %d.%d.%d.%d",

strFunc, strMsiMaj, strMsiMin, strMsiRev, strMsiBld, dwMsiMaj , dwMsiMin, dwMsiRev, dwMsiBld );

//MessageBox(GetForegroundWindow(), strMsg, "dneca", 0);

DnLogMessage( DnLog, SEV_INFO, strMsg );





// compare the versions

if((dwFileMaj < dwMsiMaj)

||((dwFileMaj == dwMsiMaj) && (dwFileMin < dwMsiMin))

||((dwFileMaj == dwMsiMaj) && (dwFileMin == dwMsiMin) && (dwFileRev < dwMsiRev))

||((dwFileMaj == dwMsiMaj) && (dwFileMin == dwMsiMin) && (dwFileRev == dwMsiRev) && (dwFileBld < dwMsiBld)))

{

MSetProperty( a_hSession, TEXT("DN_FILEVER_MISMATCH"), TEXT("1") );

DnLogMessage( DnLog, SEV_INFO, TEXT("%s has set DN_FILEVER_MISMATCH = 1, the MSI file is higher version"), strFunc);

}

else

{

DnLogMessage( DnLog, SEV_INFO, TEXT("%s has NOT set DN_FILEVER_MISMATCH, the MSI file is equal or lower version"), strFunc);

}

}

}

}

}



catch(CMSIException Ex)

{

Ex.ProcessError(a_hSession);

Ex.Delete();

MsiCloseAllHandles();

DnLogMessage( DnLog, SEV_ERROR,

TEXT("----->> %s exception Exiting... rc=%#x <<-----\n"), strFunc, ERROR_SUCCESS);

DnLogExit( DnLog );

return ERROR_SUCCESS; // no rollback

}



DnLogMessage( DnLog, SEV_INFO, TEXT("----->> %s Exiting... rc=0 <<-----\n"), strFunc);

DnLogExit( DnLog );

return ERROR_SUCCESS;

}






EdT's picture
16
Mar
2009
0 Votes 0
Login to vote

Thanks.

The most reliable way to submit code is to ZIP it first then upload and attach to your posting.

If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.