how to find out which products are installed - newer product are already installed MSI windows - wix

I can create MSI via WIX -> we installed it on IIS. What is happening - we had some version of application installed already on let's say 1.8, then we installed version let's say 99.0 just for testing purposes, then we uninstalled this 99 version. Then i tryed to install other version and obtained: A newer version of the product is already installed.
Then i tryed following changing upgrade code of product - and make high version again, then uninstall and install lower version - and it worked fine.
So i feel i missing something - additional information is that in programs and features list i cannot find that higher application after uninstall - then my question is how installer evaluate that there is newer version? where exactly are informations about what is installed(and are used for comparison) stored and how to effectively and easily access them? so i can look straight on it?

ProductCode identifies a particular product. It changes every time you ship a new replacement product.
UpgradeCode defines a series of products by using the same UpgradeCode in a updated products whose versions are expected to continually increase. By default, new product versions replace older product versions with a major upgrade. Because upgradecode defines a product series, Windows will look for products with the same UpgradeCode because identical UpgradeCodes means mutually exclusiv products, using them to replace an older product with a new one. In WiX, major upgrade is done with the majorupgrade element which it appears you may be using because you get that "a newer version is installed" message. There is an AllowDowngrade option there if you want to "upgrade" to a lower version.
Product versions (like file versions) are not just useful information - they are used by the system with the understanding that new replaces old and generally it is a bad thing to go back to lower versions, that's why the default behavior disallows downgrades.
This script might help. It uses the Windows Installer scripting API to enumerate all the installed products, showing version, user sid, ProductCode etc:
Option Explicit
Public installer, fullmsg, comp, prod, a, fso, pname, ploc, pid,contxt, sid, psorce, pcache, pvers
Set fso = CreateObject("Scripting.FileSystemObject")
Set a = fso.CreateTextFile("prodex.txt", True)
' Connect to Windows Installer object
Set installer = CreateObject("WindowsInstaller.Installer")
a.writeline ("Products")
'on error resume next
For Each prod In installer.ProductsEx("", "", 7)
pid = prod.ProductCode
contxt = prod.Context
sid=prod.usersid
pname = prod.InstallProperty("ProductName")
psorce = prod.InstallProperty("InstallSource")
ploc =prod.InstallProperty("InstallLocation")
pcache = prod.InstallProperty("LocalPackage")
pvers=prod.InstallProperty("VersionString")
a.writeline (pid & " " & pname & " " & pvers & " installed at <" & ploc & "> from " & psorce & " Context " & contxt & " Local " & pcache)
Next

Related

How to show a warning and exit if a software is not installed on WIX installer

I'm new with Wix, let me give some info about what I'm trying to accomplish. I have an installer for software "B" but this software needs to have software "A" to actually work. So I want to add on the installer for software "B" to check if software "A" is installed, if it isn't then to show a message and then exit the installer.
So below is the code of what I have tried but the message always shows even though the file is there. So I'm basically looking for a file from software "A", if its present then the install should continue normally, if it isn't then the warning message should be displayed and exit installer.
<Property Id="SOFTWARE_A_INSTALLED">
<DirectorySearch
Id="LocationFile"
Path="C:\Windows\Microsoft.NET\assembly\SOFTWAREA">
<FileSearch Name="A.dll"></FileSearch>
</DirectorySearch>
</Property>
<Condition Message="[ProductName] requires SOFTWARE A installed.">
<![CDATA[Installed OR SOFTWARE_A_INSTALLED]]>
</Condition>
Thanks
There are usually much better ways of doing this. If that product is an MSI file then use an Upgrade element to detect the product's UpgradeCode, or use the Component ID of that assembly to do a component search. Or maybe the product creates a registry key that you can search for.
In general I don't recommend your approach because you said that your product B requires A for it to work. You didn't say that your INSTALL requires A for the install to succeed, so you are creating an install order dependency when there is actually only a product dependency. So what does your product do if A is uninstalled? Crash? Give any kind of warning? A better solution might be for your app to do the check rather then create a required install order.
If that assembly from A is really a dependency of your app and it is just one of a few files you depend on, then maybe it should be a redistributable, available as something such as a merge module. People don't (for example) check if Crystal Reports files are on the system - they just include the merge module that includes the files and installs them in a way that multiple users of a system can all share the same files. The same is true of many other shared files.
This vbscript will enumerate installed component ids to check that you have the right values for a component search:
Option Explicit
Public installer, fullmsg, comp, a, prod, fso, pname, ploc, pid, psorce
Set fso = CreateObject("Scripting.FileSystemObject")
Set a = fso.CreateTextFile("comps.txt", True)
' Connect to Windows Installer object
Set installer = CreateObject("WindowsInstaller.Installer")
a.writeline ("MSI Components")
on error resume next
For Each comp In installer.components
a.writeline (comp & " is used by the product:")
for each prod in Installer.ComponentClients (comp)
pid = installer.componentpath (prod, comp)
pname = installer.productinfo (prod, "InstalledProductName")
a.Writeline (" " & pname & " " & prod & "and is installed at " & pid)
Next
Next

Using the TFS API, filetypes with extensions like .svnExe get ignored

I'm working on a tool which migrates from SVN to TFS using the TFS API.
workspace.CheckIn(
pendingChanges,
currentUser.TfsUser,
set.LogMessage + " on " + String.Format("{0:d/M/yyyy HH:mm:ss}", set.TimeStamp) + " by " + currentUser.SvnUser,
(CheckinNote)null,
(WorkItemCheckinInfo[])null,
(PolicyOverrideInfo)null
);
This is the way i check my revision in, but sometimes it ignores files like .svnExe, or other "unknown" file types.
Is there a way to check ALL filetypes in TFS?
There are two possibilities that I can think of:
Possibility 1: Something is causing the PendAdd() to fail.
For example, if the path already exists in Version Control, you have to use a PendEdit() instead.
To diagnose this possibility, you should subscribe to the VersionControlServer.NonFatalError event.
Possibility 2: You could have a corrupt workspace cache
You can refresh the cache by calling Workstation.Current.EnsureUpdateWorkspaceInfoCache() or by following the steps in this answer (run tf workspaces /collection:http://yourserver:8080/tfs/DefaultCollection, or delete the directories manually).

WiX Custom Action - MSI copy itself

Can someone help me to build a custom action in MSI which will copy itself after successful installation to some X location.
I have already seen it can be done using .exe but I want to do it only with CA.DLL (C#) as this exe will be an overhead.
Here's an example VB script that will find an installed product by name and copy the cached copy of the MSI. This will work on Windows 7 and later, as the full MSI is cached and any embedded cab files remain in the MSI. You just get the MSI without the payload on older systems.
Dim installer, products, product, productCode
Set installer = Wscript.CreateObject("WindowsInstaller.Installer")
For Each productCode In installer.Products
If InStr(1, LCase(installer.ProductInfo(productCode, "ProductName")), LCase("My Product Name")) Then Exit For
Next
If IsEmpty(productCode) Then Wscript.Quit 2
Set products = installer.ProductsEx(productCode, "", 7)
filesys.copyFile products(0).InstallProperty("LocalPackage"), "c:\path\to\newcopy.msi"

How to optimize Lucene.Net indexing

I need to index around 10GB of data. Each of my "documents" is pretty small, think basic info about a product, about 20 fields of data, most only a few words. Only 1 column is indexed, the rest are stored. I'm grabbing the data from text files, so that part is pretty fast.
Current indexing speed is only about 40mb per hour. I've heard other people say they have achieved 100x faster than this. For smaller files (around 20mb) the indexing goes quite fast (5 minutes). However, when I have it loop through all of my data files (about 50 files totalling 10gb), as time goes on the growth of the index seems to slow down a lot. Any ideas on how I can speed up the indexing, or what the optimal indexing speed is?
On a side note, I've noticed the API in the .Net port does not seem to contain all of the same methods as the original in Java...
Update--here are snippets of the indexing C# code:
First I set thing up:
directory = FSDirectory.GetDirectory(#txtIndexFolder.Text, true);
iwriter = new IndexWriter(directory, analyzer, true);
iwriter.SetMaxFieldLength(25000);
iwriter.SetMergeFactor(1000);
iwriter.SetMaxBufferedDocs(Convert.ToInt16(txtBuffer.Text));
Then read from a tab-delim data file:
using (System.IO.TextReader tr = System.IO.File.OpenText(File))
{
string line;
while ((line = tr.ReadLine()) != null)
{
string[] items = line.Split('\t');
Then create the fields and add the document to the index:
fldName = new Field("Name", items[4], Field.Store.YES, Field.Index.NO);
doc.Add(fldName);
fldUPC = new Field("UPC", items[10], Field.Store.YES, Field.Index.NO);
doc.Add(fldUPC);
string Contents = items[4] + " " + items[5] + " " + items[9] + " " + items[10] + " " + items[11] + " " + items[23] + " " + items[24];
fldContents = new Field("Contents", Contents, Field.Store.NO, Field.Index.TOKENIZED);
doc.Add(fldContents);
...
iwriter.AddDocument(doc);
Once its completely done indexing:
iwriter.Optimize();
iwriter.Close();
Apparently, I had downloaded a 3 yr old version of Lucene that is prominently linked to for some reason from the home page of the project...downloaded the most recent Lucene source code, compiled, used the new DLL, fixed about everything. The documentation kinda sucks, but the price is right and its real fast.
From a helpful blog
First things first, you have to add
the Lucene libraries to your project.
On the Lucene.NET web site, you’ll see
the most recent release builds of
Lucene. These are two years old. Do
not grab them, they have some bugs.
There has not been an official release
of Lucene for some time, probably due
to resource constraints of the
maintainers. Use Subversion (or
TortoiseSVN) to browse around and grab
the most recently updated Lucene.NET
code from the Apache SVN Repository.
The solution and projects are Visual
Studio 2005 and .NET 2.0, but I
upgraded the projects to Visual Studio
2008 without any issues. I was able
to build the solution without any
errors. Go to the bin directory, grab
the Lucene.Net dll and add it to your
project.
Since I can't comment on the marked answer above related to a 3 year old version, I would highly recommend installing the Visual Studio extension for NuGet Package Manager when adding Lucene.NET to your projects. It should add the most recent DLL version for you unless you need a specific later version.

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).