MSI product property information - c++

I have following xml for wix configuration of the MSI..
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UIExtension">
<Product Id="*"
Name="MyProductName"
Language="1033"
Version="1.0.0"
Manufacturer="Stack"
Codepage="1200"
UpgradeCode="242C9452-1B5E-414A-8245-D86B14E8E9CC">
<Package Id="*"
InstallerVersion="405"
Compressed="yes"
InstallScope="perMachine"
Description="MyProduct"
/>
<Property Id="Company" Value="$(var.Company)"/>
</Product>
</Wix>
And I am using below code (QT C++) to code to get the property information
#include <Windows.h>
#include <Msi.h>
#include <MsiQuery.h>
LPCWSTR program = L"C:/installer.msi";
MSIHANDLE hProduct = NULL;
LPWSTR pszVersion = NULL;
LPDWORD dwSizeVersion = NULL;
LPCWSTR property = L"ProductVersion";
MsiOpenPackage( program, &hProduct );
MsiGetProductProperty( hProduct, property, pszVersion, dwSizeVersion );
MsiCloseHandle( hProduct );
qDebug() << "MSI Property version is : " << QString::fromWCharArray(pszVersion);
I have tried adding ProductVersion to field property in the code, but the code is not returning the value provided in the XML. How can I get the Product Version, Package InstallVersion and Company property information from the MSI.

The docs say that your last parameter should be the size of the buffer where the result is going to be stored, not NULL.

Related

WebM Dash on Amazon Media Convert - No Segment Range

We're using Amazon Media Convert to create MPDs for playing WebMs. The manifest looks like this
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:cenc="urn:mpeg:cenc:2013" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd" type="static" minBufferTime="PT12S" profiles="urn:mpeg:dash:profile:isoff-main:2011" mediaPresentationDuration="PT2.474S">
<Period start="PT0S" duration="PT2.474S" id="1">
<AdaptationSet mimeType="video/webm" frameRate="60000/1001" segmentAlignment="true" subsegmentAlignment="true" startWithSAP="1" subsegmentStartsWithSAP="1" bitstreamSwitching="false">
<Representation id="1" width="1920" height="1080" bandwidth="4000000" codecs="vp9">
<BaseURL>webm.2.40.webm.VP9.webm</BaseURL>
</Representation>
</AdaptationSet>
<AdaptationSet mimeType="audio/mp4" lang="eng" segmentAlignment="0">
<Representation id="2" bandwidth="96000" audioSamplingRate="48000" codecs="mp4a.40.2">
<BaseURL>webm.audio.mp4</BaseURL>
<SegmentBase indexRange="658-709">
<Initialization range="0-657"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
With the following Codec settings.
new VideoCodecSettings()
{
Codec = VideoCodec.VP9,
Vp9Settings = new Vp9Settings()
{
Bitrate = bitRate,
FramerateControl = Vp9FramerateControl.INITIALIZE_FROM_SOURCE,
RateControlMode = Vp9RateControlMode.VBR,
FramerateConversionAlgorithm =
Vp9FramerateConversionAlgorithm.DUPLICATE_DROP,
}
};
When loading in DashJS I get the following error:
WebmSegmentBaseLoader.js:298 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'split')
at WebmSegmentBaseLoader.js:298:67
at new Promise (<anonymous>)
at Object.loadInitialization (WebmSegmentBaseLoader.js:295:16)
at Object.getSegmentBaseInitSegment (SegmentBaseController.js:97:42)
at Object.updateInitData (SegmentsController.js:63:38)
at RepresentationController.js:135:46
at new Promise (<anonymous>)
at _updateRepresentation (RepresentationController.js:127:16)
at Object.updateData (RepresentationController.js:120:27)
at Object.selectMediaInfo (StreamProcesso
When it runs let initRange = representation ? representation.range.split('-') : null;
I assume it's because there is no ranges in the adaption set.
My question is how to get WebM DASH MPDs with Ranges?

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
How to get the path in WIX and send it to the C++ Custom Action
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.

WiX Heat.exe generates different output after VS2008 -> VS2015 upgrade

We execute the following command line with heat (3.0.5210.0):
heat.exe -srd -suid file OurLib.dll -out bin_OurLib.tmp
OurLib.dll is a VC++ 2008 dll file, it's a COM component.
Output (bin_OurLib.tmp)
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="OurLib.dll" Guid="PUT-GUID-HERE">
<File Id="OurLib.dll" KeyPath="yes" Source="SourceDir\OurLib.dll">
<Class Id="{B1AB297C-1BC6-65E1-A7C1-A1833DFAED6A}" Context="InprocServer32" Description="OurProduct.OurLib">
<ProgId Id="OurProduct.OurLib" Description="OurProduct.OurLib" />
</Class>
</File>
</Component>
</DirectoryRef>
</Fragment>
</Wix>
This works fine.
Now we updated the VS to 2015, and compiled OurLib with it.
Running the same heat command now results in different output:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="OurLib.dll" Guid="PUT-GUID-HERE">
<Class Id="{B1AB297C-1BC6-65E1-A7C1-A1833DFAED6A}" Context="InprocServer32" Description="OurProduct.OurLib">
<ProgId Id="OurProduct.OurLib" Description="OurProduct.OurLib" />
</Class>
<File Id="OurLib.dll" KeyPath="yes" Source="SourceDir\OurLib.dll" />
<RegistryValue Root="HKCR" Key="CLSID\{B1AB297C-1BC6-65E1-A7C1-A1833DFAED6A}\InProcServer32" Value=""[#OurLib.dll]"" Type="string" Action="write" />
</Component>
</DirectoryRef>
</Fragment>
</Wix>
And when wix is linking, it gives error:
error LGHT0130 : The primary key 'reg51A1FC16367511AF81E9B18CA009A1C6' is duplicated in table 'Registry'. Please remove one of the entries or rename a part of the primary key to avoid the collision.
Checking the wixobj file, reg51A1FC16367511AF81E9B18CA009A1C6 is related to line
<Class Id="{B1AB297C-1BC6-65E1-A7C1-A1833DFAED6A}...." and <RegistryValue Root="HKCR" Key="CLSID\{B1AB.... - obviously.
Anwyay, this is obviously due to the generated wix output files are different.
Why is that if compiling a dll with a newer C++ compiler will cause WIX HEAT to generate different output, while running the same options, etc.
And how can I have back the same "old" output, which we need.
Tried with -scom and -sreg, none of them gave back the "old" output.
Well primary key duplicated means you've got that same RegistryValue in two different places in your wix source code somehow.
Or maybe the newer DLL is self-registering differently? It's putting the InProcServer32 value in quotes. I don't think that's normal.
Or have you checked that your 2008 and 2015 DLL source and project files are identical? The VS project upgrade process changes things...

Slowcheetah app.config pass parameters to transform file

I have following transform file app.Release.config to transform my app.config using SlowCheetah. How do I pass the BUILD_NUMBER from TeamCity so that the transform file replaces a particular xml elements value.
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyApp.Data.Model" publicKeyToken="866d4a0fa0599fe0" culture="neutral" />
<bindingRedirect name="MyApp.Data.Model.BR" oldVersion="0.0.0.0-$(BUILD_NUMBER)" newVersion="$(BUILD_NUMBER)" xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</dependentAssembly>
</assemblyBinding>
If I hard code the value then SlowCheetah does the transformation. But, I don't know how to pass in the build_number as a argument so that for each build I can replace the correct version.
I could not find a way to pass parameters to slowcheetah transform files. I'm using a XmlPoke Task to modify the contents of app.config.
My answer for this can be found at this link

apigee policy error - java.lang.String cannot be cast to com.apigee.flow.message.Message

I am trying to transform a JSON response from a target end point into soap message. I have 2 policies in the post flow.
JSONTOXML - If I disable xsltransform I see the xml result so this work.
xsltransform - this just point to my xsltransform file which has the template and dynamic content supposed to come from the xml from step 1.
Both source and output are set to "response" but it looks like it failed to cast in step 2. What should I make step 2 to pick up the xml and apply the xsl? How else can I do this? Thanks
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JSONToXML async="false" continueOnError="false" enabled="true" name="jsontoxml-2">
<DisplayName>JSONtoXML-2</DisplayName>
<FaultRules/>
<Properties/>
<Options>
<NullValue>NULL</NullValue>
<NamespaceBlockName>#namespaces</NamespaceBlockName>
<DefaultNamespaceNodeName>$default</DefaultNamespaceNodeName>
<NamespaceSeparator>:</NamespaceSeparator>
<TextNodeName>#text</TextNodeName>
<AttributeBlockName>#attrs</AttributeBlockName>
<AttributePrefix>#</AttributePrefix>
<InvalidCharsReplacement>_</InvalidCharsReplacement>
<ObjectRootElementName>Root</ObjectRootElementName>
<ArrayRootElementName>Array</ArrayRootElementName>
<ArrayItemElementName>Item</ArrayItemElementName>
</Options>
<OutputVariable>response</OutputVariable>
<Source>response</Source>
</JSONToXML>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XSL async="false" continueOnError="false" enabled="true" name="xsltransform-1">
<DisplayName>XSLTransform-1</DisplayName>
<FaultRules/>
<Properties/>
<Source>response</Source>
<ResourceURL>xsl://xsltransform-1</ResourceURL>
<Parameters ignoreUnresolvedVariables="true"/>
<OutputVariable>response</OutputVariable>
</XSL>
OutputVariable cannot be set to 'response'.
Try removing the 'OutputVariable' line, or leaving it empty. Removing it, should cause the output to go to the response payload.
In other words, try this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XSL async="false" continueOnError="false" enabled="true" name="xsltransform-1">
<DisplayName>XSLTransform-1</DisplayName>
<FaultRules/>
<Properties/>
<Source>response</Source>
<ResourceURL>xsl://xsltransform-1</ResourceURL>
<Parameters ignoreUnresolvedVariables="true"/>
</XSL>
Is this always the case? Can OutputVariable never be set on response?