简体   繁体   中英

How do I use the OrignalDatabase from MSI Installer in WIX and pass it to the C++ custom action code

Introduction: I have a codebase being used for the updation of the MSI and that I am doing using the MD5 hash and every-time an update arrives at server, the code has functionalities that check the MD5 from DB and if it doesn't match then update it but for the first time when a new user installs it I need to find the current hash of the MSI at runtime, since user can change it's location and name and it can result in hash changes.

Problem Statement: I have the WIX Installer for my MSI and I want to get to get the [Original Database] property from the WIX and send it to the C++ Custom Action DLL for generating MD5 at runtime, I am looking to do it post installation because the Database will be setup after the installation and the app starts running. So I can store the MD5 in a temporary file or do it post installation. I know other people also have questions already posted on StackOverflow regarding OriginalDatabase but I couldn't find them quite satisfactory and addressing the problem that I am facing and there was no proper code example for this and so I am writing this question.

The problem I am facing are these

  1. How to get the path in WIX and send it to the C++ Custom Action
  2. How to store the MD5 in the DB through WIX, though this is not too important but if I can get a solution for this, it would be great.

WIX Code:

<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
    xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">

<Product Id="{}" UpgradeCode="{}" 
        Name="Secureone Agent" Version="1.0.0" Manufacturer="MJ Inc." Language="1033">

    <Package InstallerVersion="200" Compressed="yes" Comments="MJ Client Installer Package" Platform="x64"/>
    <Media Id="1" Cabinet="product.cab" EmbedCab="yes"/>

    <!-- Pre-Install checks -->

    <Condition Message="This application is only supported on Windows Vista, Windows Server 2008, or higher.">
        <![CDATA[Installed OR (VersionNT >= 600)]]>
    </Condition>

    <Condition Message="Setup can not be installed on x32 machine.">
      <![CDATA[Not VersionNT32]]>
    </Condition>

    
    <?define Platform=x64 ?>
    <?define BIN_PATH='.\bin' ?>
    <?if $(var.Platform) = x64 ?>
    <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
    <?else ?>
    <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
    <?endif ?>

    
    <!-- License agreement -->
    <WixVariable Id="WixUILicenseRtf" Value=".\eula\eula-en.rtf" />

    <!--Application logo-->
    <Icon Id="Logo.ico" SourceFile=".\resources\logo.ico" />

    <!--Application logo for Control Panel-->
    <Property Id="ARPPRODUCTICON" Value="Logo.ico" />

    <!--Top Banner UI Logo-->
    <WixVariable Id="WixUIBannerBmp" Overridable="yes" Value=".\resources\TopBanner.jpg" />
    <!--Verticle Banner UI Logo-->
    <WixVariable Id="WixUIDialogBmp" Overridable="yes" Value=".\resources\BackgroundLogo.jpg" />
    
    <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="$(var.PlatformProgramFilesFolder)">
            <Directory Id="INSTALLDIR" Name="Secureone">
               <Component Id="ServiceConfigFile" Guid="{}">
                  <File Id="ServiceConfigFile" Source="$(var.BIN_PATH)\core.db"/>
               </Component>
               <Component Id="ServiceFile" Guid="{}">
                  <File Id="ServiceFile" Source="$(var.BIN_PATH)\lzsvc.exe"/>
<!-- Install service -->
                  <ServiceInstall Id="ServiceInstaller"
                                    Type="ownProcess"
                                    Name="LZAgentService"
                                    DisplayName="LZ Agent Service"
                                    Description="Service for LZ Agent"
                                    Start="auto"
                                    ErrorControl="normal"
                                    Account="LocalSystem">
                        <util:ServiceConfig 
                                            FirstFailureActionType="restart"
                                            SecondFailureActionType="restart"
                                            ThirdFailureActionType="none"
                                            ResetPeriodInDays="1"
                                            RestartServiceDelayInSeconds="0" />
                    </ServiceInstall>
                    <ServiceControl Id="Service_start" Start="install" Stop="both" Name="LZAgentService" Wait="no" />
                    <ServiceControl Id="Service_stop" Stop="both" Remove="uninstall" Name="LZAgentService" Wait="no" />
<!-- Install service end -->    
                </Component>
            </Directory>
        </Directory>
    </Directory>
    
    <UIRef Id="WixUI_Minimal"/>

    <Feature Id="DefaultFeature" Level="1">
        <ComponentRef Id="ServiceFile"/>
        <ComponentRef Id="ServiceConfigFile"/>
        <ComponentRef Id="ProductComponent" />
    </Feature>
    <InstallExecuteSequence>
        <Custom Action="RollbackCA" After="InstallFiles" />
        <Custom Action='UserAuth' After='InstallFiles'/>
    </InstallExecuteSequence>
</Product>

<Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLDIR">
        <Component Id="ProductComponent" Guid="{B2C908AF-C09A-41D0-92D1-AC7BEAC8F68D}">
            <RemoveFile Id='lzsvc_log' On='uninstall' Name='lzsvc.log' />
            <RemoveFile Id='lzsvc.log.bak' On='uninstall' Name='lzsvc.log.bak' />
            <RemoveFile Id='lzcore_log' On='uninstall' Name='lzcore.log' />
         </Component>
    </ComponentGroup>
</Fragment>
   
<Fragment>
    <CustomAction Id='UserAuth'
                  BinaryKey='AuthBinary'
                  DllEntry='AuthenticateUser'
                  Execute='deferred'
                  Return='check'
                  Impersonate="yes"
                />
                
    <CustomAction Id='RollbackCA'
                      BinaryKey='AuthBinary'
                      DllEntry='RollbackInstall'
                      Execute='rollback'
                      Return='check'
                      Impersonate="yes"
                />

    <Binary Id='AuthBinary' SourceFile='$(var.BIN_PATH)\CustomAction.dll'/>
</Fragment>   
   
</Wix>

So the answer was actually a bit tricky at first but then I found it as my senior helped in it, so here it is. Currently this code finds the current path of the MSI on runtime and writes the MD5 of it in the C:\Windows\Temp\Path.txt but don't forget to include the md5 and md5wrapper header files and cpp files in the project.

C++ Custom Action:

void MD5ToDB(MSIHANDLE hInstall)
{
    TCHAR* szValueBuf=NULL;
    DWORD cchValueBuf = 0;
    UINT uiStat = MsiGetProperty(hInstall, TEXT("OriginalDatabase"), TEXT(""), &cchValueBuf);
    //cchValueBuf now contains the size of the property's string, without null termination
    if (ERROR_MORE_DATA == uiStat)
    {
        ++cchValueBuf; // add 1 for null termination
        szValueBuf = new TCHAR[cchValueBuf];
        if (szValueBuf)
        {
            uiStat = MsiGetProperty(hInstall, TEXT("OriginalDatabase"), szValueBuf, &cchValueBuf);
        }
        
    }
    if (ERROR_SUCCESS != uiStat)
    {
        if (szValueBuf != NULL)
            delete[] szValueBuf;
        return;
    }

    //Convert tcahr to char
    
    char* charWindowsPropertyValue = NULL;
    charWindowsPropertyValue = new CHAR[cchValueBuf];
    wcstombs(charWindowsPropertyValue, szValueBuf, wcslen(szValueBuf) + 1);//Converting tchar to char
    std::string filePath(charWindowsPropertyValue);//converting char array to string
    md5wrapper md5;
    std::string hash = md5.getHashFromFile(filePath);//claculating md5 of the file path as string.
    FILE* fp=NULL;
    fopen_s(&fp,"C:\\Windows\\Temp\\Path.txt", "w+");//creating and writing to Path.txt
    if (NULL != fp) {
        fwrite(hash.c_str(), hash.length(), 1, fp);
        fclose(fp);
    }
    
}



UINT __stdcall ExtractingProperty(
    MSIHANDLE hInstall
)
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    hr = WcaInitialize(hInstall, "ExtractingProperty");
    ExitOnFailure(hr, "Failed to initialize");

    WcaLog(LOGMSG_STANDARD, "Initialized.");
    MD5ToDB(hInstall);


LExit:
    er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
    return WcaFinalize(er);
}
//Code was mostly taken from MSDN and the main attempt was to get the MD5 of the path after obtaining the path.

WIX File

<Custom Action="MD5ToDB" After"<Some Action>"/>

<CustomAction Id="MD5ToDB" 
                    BinaryKey="AuthBinary" 
                    DllEntry="ExtractingProperty" 
                    Execute="immediate" 
                    Return="check" />
    

Don't forget to add the function name in WIX .def file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM