WiX Light.exe outputs "LGHT0204 : ICE80: This 32BitComponent [component name] uses 64BitDirectory [directory name]" for every wxs component - wix

During the creation of an msi file with Light.exe (without Visual Studio), I got a warning for every wxs component in my project.
The errors looked like this:
C:\path\WXS.wxs(10) : error LGHT0204 : ICE80: This 32BitComponent [component name] uses 64BitDirectory [directory name]
As far as I could tell, there were no 32 bit components among the files and Platform='x64' was set in the base wxs file.
Tried drinking coffe and crying to very little effect.

Solved using the recommendation of:
https://wixtoolset.org/docs/v3/xsd/wix/package/
The description of the package suggested the usage of the "-arch" switch directly with light.exe instead of specifying it with the Platform attribute.

Related

BURN: Logging BURN_PACKAGE-> sczLogPathVariable to be used to create complete Log file

I am using WIX 3.7, and I am wanting to have my MSI and BURN log files, be created at my desired location. I tried verbatim both the approaches, mentioned in How to set or get all logs in a custom bootstrapper application newsgroup post. However, Log File gets created in the default location. Since I had time on hand, I decided to explore WIX 3.7 through WIX 3.9 Source Code, attempting to find where BURN_PACKAGE-> sczLogPathVariable data member is used.
However I found just 5 references to BURN_PACKAGE-> sczLogPathVariable
\wix38-debug\src\burn\engine\logging.cpp(191):
if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) ||
\wix38-debug\src\burn\engine\logging.cpp(197): hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE);
\wix38-debug\src\burn\engine\package.cpp(152):
hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable);
\wix38-debug\src\burn\engine\package.cpp(303):
ReleaseStr(pPackage->sczLogPathVariable);
\wix38-debug\src\burn\engine\package.h(165):
LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path.
I was expecting some code that would actually retrieve the value of MY VARIABLE and then CONCATENATE that value with the Log File Name that was synthesized, to create the complete file path. Maybe I am missing something obvious ? Do you guys have any suggestions ?
*Package/#LogPathVariable is used to specify a variable that gets the path to the log. To control the logging, use the Log element.

Use a C++/CLI DLL using afxwinforms.h as Reference of another C++/CLI DLL also using afxwinforms.h

In Visual C++ 2010 i added a reference from a C++/CLI DLL (ControlWrapper.dll) to another C++/CLI DLL (CliLibrary.dll).
Both are including afxwinforms.h in the stdafx.h.
When i try to compile i get these errors:
error C2011: 'Microsoft::VisualC::MFC::CWin32Window' : 'class' type redefinition
error C2011: 'Microsoft::VisualC::MFC::CWinFormsEventsHelper' : 'class' type redefinition
If i turn of the Option Reference Assembly Output and add #using "CliLibrary.dll" to the using .cpp File i get the following warnings:
1>ControlWrapper.dll : warning C4944: 'CWin32Window' : cannot import symbol from 'c:\dev\trunk\CliLibrary.dll': as 'Microsoft::VisualC::MFC::CWin32Window' already exists in the current scope
1> C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\include\afxwinforms.h(83) : see declaration of 'Microsoft::VisualC::MFC::CWin32Window'
1> This diagnostic occurred while importing type 'Microsoft.VisualC.MFC.CWin32Window' from assembly 'CliLibrary, Version=1.0.4843.17337, Culture=neutral, PublicKeyToken=null'.
1>ControlWrapper.dll : warning C4944: 'CWinFormsEventsHelper' : cannot import symbol from 'c:\dev\sfirm\trunk\sfclrlib\debug\sfclrlib.dll': as 'Microsoft::VisualC::MFC::CWinFormsEventsHelper' already exists in the current scope
1> C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\include\afxwinforms.h(122) : see declaration of 'Microsoft::VisualC::MFC::CWinFormsEventsHelper'
1> This diagnostic occurred while importing type 'Microsoft.VisualC.MFC.CWinFormsEventsHelper' from assembly 'CliLibrary, Version=1.0.4843.17337, Culture=neutral, PublicKeyToken=null'.
How could i solve the error?
Well, this is a painful problem. It certainly explains why you are the first programmer I ever encountered that actually uses this. The problem is caused by this declaration in afxwinforms.h:
public ref class CWin32Window : public System::Windows::Forms::IWin32Window
// etc..
The public keyword is the killer, that adds the class to the manifest of your assembly. So when you reference it in another project that also includes the header then there are two definitions of the class. The mix of both native and managed classes in that header prevents a clean solution.
I think you already found the best solution, using the #include, with #pragma comment(disable:4944) to shutup the compiler. Including the header inside a namespace might be another viable hack, it renames the namespace of CWin32Window, but I'd expect trouble when linking mfcm90.lib. Restructuring your solution and keeping all winforms code inside one project is the only thing I can recommend.
Are you using the types in afxwinforms.h?
If not (like in my case), the solution is to comment the include and add this two lines:
#using <System.Windows.Forms.dll>
#using <System.Drawing.dll>
or add a reference to that two assemblies in your Project.

How do I pass MSBuild arguments from the build definition to a MSBuild workflow Activity

I've defined an MSBuild activity in a TFS Workflow template, but currently I have to hard code the 'property' command line arguments directly into the template.
I'd like to be able to specify the arguments in the build definition, via the advanced setting, 'MSBuild Arguments'
I can see that I may have to build up the command line with string replace/concat, as mentioned here, but I can't see what I need to put, maybe something like this:
This is what the default MsBuild task uses:
String.Format("/p:SkipInvalidConfigurations=true {0}", MSBuildArguments)
You can change the MSBuildArguments variable in the build process template in multiple steps. For example, I added a Run Architecture Validation property to the process template and then edited the workflow to simply append /ValidateArchitecture=true to the MSBuildArguments before they're being passed to the MsBuild activity.
<If Condition="[PerformArchitectureValidation]" DisplayName="Configure Architecture Validation MSBuild Arguments">
<If.Then>
<Assign>
<Assign.To>
<OutArgument x:TypeArguments="x:String">[MSBuildArguments]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:String">[MSBuildArguments + " /p:ValidateArchitecture=true"]</InArgument>
</Assign.Value>
</Assign>
</If.Then>
</If>
The PerformArchitectureValidation variable is defined as a Property on the Build Process Template level of type Boolean.
Update: Wrote a blogpost that explains this with steps and screenshots

How to reference SystemFolder in WiX Icon.SourceFile property?

To make the long story short, this doesn't work:
<Icon Id="msiexec.ico" SourceFile="[SystemFolder]msiexec.exe"/>
(Error 4 The system cannot find the file '[SystemFolder]msiexec.exe')
And this doesn't work too:
<Icon Id="msiexec.ico" SourceFile="$(var.SystemFolder)msiexec.exe"/>
Error 3 Undefined preprocessor variable '$(var.SystemFolder)'.
The second sample in your question will work if you pass var.SystemFolder as a parameter to candle.exe.
The <Icon> element is mapped to the Icon MSI table. At build time it tries to find the path you specify in SourceFile attribute and stream it as binary data to the Data column of the Icon table. This means, the path should be known at build time. But this is not true in your first sample - SystemFolder is resolved at install time.

How can I programmatically read the properties inside an MSI file?

Is there a way to read the properties inside an MSI file?
For example, given a MSI file named Testpackage.msi, I need to find
productName
PackageCode
version
This I am going to use it with WMI uninstall
string objPath = string.Format("Win32_Product.IdentifyingNumber='{0}', Name='{1}', Version='{2}'", "{AC9C1263-2BA8-4863-BE18-01232375CE42}", "testproduct", "10.0.0.0");
Using Orca is a great option, if this can be achieved programmatically. Then I can use this to generate automatic release notes. And in un-installing program too.
You can use the COM-based API for working with MSI, and do something like
Function GetVersion(ByVal msiName)
Const msiOpenDatabaseModeReadOnly = 0
Dim msi, db, view
Set msi = CreateObject("WindowsInstaller.Installer")
Set db = msi.OpenDataBase(msiName, msiOpenDatabaseModeReadOnly)
Set view = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'")
Call view.Execute()
GetVersion = view.Fetch().StringData(1)
End Function
WiX toolset: WiX quick-start tips (collection of links to resources). WiX installs DTF.
I just want to mention that things have gotten even easier now. There is a full .NET wrapper for the Windows Installer object model, so you can avoid any COM interop clunkiness.
DTF - Getting Started: Main file: Microsoft.Deployment.WindowsInstaller.dll
Download and install the WiX toolkit
Find the files below in the WixInstallPath\SDK directory
The wrapper is called "Deployment Tools Foundation" (DTF) and here is the basic description: "Deployment Tools Foundation is a rich set of .NET class libraries and related resources that together bring the Windows deployment platform technologies into the .NET world. It is designed to greatly simplify deployment-related development tasks while still exposing the complete functionality of the underlying technology".
Here is a stripped-down, hands-on sample:
using (var db = new Database(FullPath, DatabaseOpenMode.ReadOnly))
{
PackageCode = db.SummaryInfo.RevisionNumber;
AppVendor = db.SummaryInfo.Author;
AppName = db.SummaryInfo.Title;
ProductName = db.SummaryInfo.Subject;
ProductCode = (string)db.ExecuteScalar("SELECT `Value` FROM "+
"`Property` WHERE `Property` = 'ProductCode'");
AppVersion = (string)db.ExecuteScalar("SELECT `Value` FROM "+
"`Property` WHERE `Property` = 'ProductVersion'");
UpgradeCode = (string)db.ExecuteScalar("SELECT `Value` FROM "+
" `Property` WHERE `Property` = 'UpgradeCode'");
}
Primary DTF files (the latter two are the most used ones):
Microsoft.Deployment.Compression.dll - Framework for archive packing and unpacking.
Microsoft.Deployment.Compression.Cab.dll - Implements cabinet archive packing and unpacking.
Microsoft.Deployment.Resources.dll - Classes for reading and writing resource data in executable files.
Microsoft.Deployment.WindowsInstaller.dll - Complete .NET based class library for the Windows Installer APIs.
Microsoft.Deployment.WindowsInstaller.Package.dll - Extended classes for working with Windows Installer installation and patch packages.
Just create a C# project, reference these files, and code your own deployment application with whatever control you desire and need. I am not set up with the tools for DTF at the moment, but see this sample for a general idea of how a C# program would work.
DTF is included with WIX. Download WiX from here.
The DTF dlls are in the SDK folder in the main WiX installation folder (the default location is: %ProgramFiles(x86)%\WiX Toolset v3.10\SDK). The version number will probably be different by the time you see this. Just look for the WiX folder under %ProgramFiles(x86)%.
Look for the DTF help files in the "doc" folder. DTF.chm and DTFAPI.chm. Absolutely excellent documentation for the object model and its usage.
See this serverfault.com post for some more DTF details
Some starter suggestions for working with WiX
You can use Microsoft's Orca.exe. Orca will allow you to open the MSI and edit/view all the tables in it. You will have to download the entire Windows SDK in order to get it, but thankfully that is free.
One alternative (which might be faster due to the download size of the SDK) is to use dark.exe from the WiX project. Dark is a MSI decompiler, which will export everything into an XML file and collection of resources. The XML it outputs will have the information you are looking for.
Here's a similar example in VBScript which I use as part of my build process in creating bootstrapper executables...
Option Explicit
Const MY_MSI = "product.msi"
Dim installer, database, view, result, sumInfo, sPackageCode
Set installer = CreateObject("WindowsInstaller.Installer")
Set database = installer.OpenDatabase (MY_MSI, 0)
Set sumInfo = installer.SummaryInformation(MY_MSI, 0)
sPackageCode = sumInfo.Property(9) ' PID_REVNUMBER = 9, contains the package code.
WScript.Echo "ProductVersion=" & getproperty("ProductVersion")
WScript.Echo "ProductCode=" & getproperty("ProductCode")
WScript.Echo "PackageCode=" & sPackageCode
WScript.Echo "ProductName=" & getproperty("ProductName")
Function getproperty(property)
Set view = database.OpenView ("SELECT Value FROM Property WHERE Property='" & property & "'")
view.Execute
Set result = view.Fetch
getproperty = result.StringData(1)
End Function
I found a lightweight non-programmatic solution in lessmsi. It apparently uses wix and just explodes the whole .msi into a specified folder. (It also has a UI but it didn't render great for me on Win7).