Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Manualy extracting files from MSI packages

178 views
Skip to first unread message

Nadav

unread,
Nov 22, 2004, 10:47:05 AM11/22/04
to
Hi,

Introduction:
***************************
I am using the MSI API to extract MSI embedded files, I do this by iterating
through all of the records in the ‘_Streams’ table and dumping each to a
local directory on the disk, the following illustrate this:
01)MsiDatabaseOpenView(m_hMSI,
L"SELECT `Name`,`Data` FROM `_Streams`",
&hStreamsView)
02)MsiViewExecute(hStreamsView, NULL);
for each record in the _Streams table
03)MsiViewFetch(hStreamsView, &hStreamRecord)
04) MsiRecordGetString(hStreamRecord, 1,
pPackedFileName, &dwBufSize)
05)CreateFile
While there is still data in the stream
06)MsiRecordReadStream(hStreamRecord, 2,
(char*)pbtStream, &dwBufSize);
07)WriteFile

The problem:
****************************
Some MSI installation has the files embedded in a CAB file named as a GUIDs,
this CAB contain the installation files, the files are named as GUIDs
resembling the data held in the ‘File’ field of the ‘File’ table…

My Question:
****************************
Is it possible to use the MSI API to extract the ‘real’ file directly from
the MSI? Is there a method for extracting embedded files according to the
‘FileName’ column of the ‘File’ table?
My alternative is to manually dump the ‘_Streams’ table ( as I do today ),
then to identify the CAB file according to it’s signature ( I have to
identify the file according to it’s signature as the name of the CAB is a
GUID… ), extract it and to rename the extracted files according to the
File->FileName mapping provided by the ‘File’ table.
I AM SURE THERE IS AN ELEGANTH WAY TO DO THAT… What it is?
--
Nadav
http://www.ddevel.com

Dennis Bareis

unread,
Nov 22, 2004, 6:23:27 PM11/22/04
to
Hi,

"Nadav" <Na...@discussions.microsoft.com> wrote in message news:<A87141DB-6324-4810...@microsoft.com>...


> Is it possible to use the MSI API to extract the ‘real’ file directly from
> the MSI?

You need to do an "adminsitrative install" (the "/a" switch on
MSIEXEC.EXE for example).

Bye,
Dennis

Nair@discussions.microsoft.com Suraj Nair

unread,
Nov 22, 2004, 10:51:01 PM11/22/04
to
all you need to do is run msiexec /a <path to msi>.

this should extract all your embedded files.

Alex Ivanov

unread,
Nov 22, 2004, 11:44:50 PM11/22/04
to
This works if the msi has the AdminExecute sequence, but what if it hasn't?

--
Please reply to NG only. This email is not monitored.
Alex.


"Suraj Nair" <Suraj Na...@discussions.microsoft.com> wrote in message
news:4A330879-9130-4283...@microsoft.com...

>> resembling the data held in the 'File' field of the 'File' table.


>>
>> My Question:
>> ****************************
>> Is it possible to use the MSI API to extract the 'real' file directly
>> from
>> the MSI? Is there a method for extracting embedded files according to the
>> 'FileName' column of the 'File' table?
>> My alternative is to manually dump the '_Streams' table ( as I do
>> today ),
>> then to identify the CAB file according to it's signature ( I have to
>> identify the file according to it's signature as the name of the CAB is a

>> GUID. ), extract it and to rename the extracted files according to the


>> File->FileName mapping provided by the 'File' table.

>> I AM SURE THERE IS AN ELEGANTH WAY TO DO THAT. What it is?
>> --
>> Nadav
>> http://www.ddevel.com


Nadav

unread,
Nov 23, 2004, 2:59:04 AM11/23/04
to
Hi Alex,

Thanks for your immediate responce, Is there any alternative solution for
extracting the MSI using the /a option? ( something to do with the MSI API )

Alex Ivanov

unread,
Nov 24, 2004, 12:10:09 AM11/24/04
to
I did not work with GUIDed file names, but perhaps you could search msi
tables (property, files?) for GUID of interest and find the real file names
from corresponding records.

--
Please reply to NG only. This email is not monitored.
Alex.


"Nadav" <Na...@discussions.microsoft.com> wrote in message
news:45C052A7-CAD4-4B1F...@microsoft.com...

Nadav

unread,
Nov 24, 2004, 5:35:03 AM11/24/04
to
Well, this is possible BUT still I would have to implement my own CAB
extraction mechanism...

Dennis Bareis

unread,
Nov 24, 2004, 8:48:27 PM11/24/04
to
Hi,

"Alex Ivanov" <con...@collegeclub.com> wrote in message news:<OX9PmcR...@TK2MSFTNGP14.phx.gbl>...


> This works if the msi has the AdminExecute sequence, but what if it hasn't?

Then you could add it...

from the doco (Suggested AdminExecuteSequence):
Action Condition Sequence
CostInitialize 800
FileCost 900
CostFinalize 1000
InstallValidate 1400
InstallInitialize 1500
InstallAdminPackage 3900
InstallFiles 4000
InstallFinalize 6600

Bye,
Dennis

"Martin v. Löwis"

unread,
Nov 25, 2004, 1:22:52 AM11/25/04
to
Nadav wrote:
> 05)CreateFile

Why can't you just use the real file name here?

Notice, however, that you also need to build the directory hierarchy,
as file names might be duplicated (in different directories).

Regards,
Martin

Nadav

unread,
Nov 25, 2004, 3:11:01 AM11/25/04
to
> Why can't you just use the real file name here?
Well, MSI packages created by VS.NET automatically pack the setup files to a
.CAB file, the names of the files contained in this CAB are GUIDs ( and not
their original names ), the name of the .CAB file is also a GUID ( and not a
meaningful name ), however it is possible to manually extract the files (
although it is a cumbersome way ), following are the requiered steps:
1. Dump the files found in '_Streams' table to a temporary directory
2. Identify the CAB file according to it's signature ( and not it's
extention: as the CAB name is a GUID )
3. Manually extract the CAB file to another temporary directory
4. The resuting files names are GUIDs ( and not their original names ),
rename them to their original names according to the GUID<->Filename mapping
found in the 'Files' table.
[*] What was just described IS ACHIEVABLE, B U T, I think it is more then
probable that there is a 'cleaner' solution, one that enable direct
extraction of the files ( with out doing the actual installation of each file
).
IS THERE ANY CLEANER SOLUTION ???

Nadav

unread,
Nov 25, 2004, 3:15:06 AM11/25/04
to
Does usage of AdminExecute sequence install the files or just extract them to
the disk? I don't want to install the files...

"Martin v. Löwis"

unread,
Nov 25, 2004, 12:20:09 PM11/25/04
to Nadav
Nadav wrote:
> Does usage of AdminExecute sequence install the files or just extract them to
> the disk? I don't want to install the files...

Why don't you try and see for yourself, or read the documentation on
what precisely an administrative install does?

Regards,
Martin

Alan Klietz

unread,
Nov 29, 2004, 9:12:10 AM11/29/04
to
In news:996F9583-5609-4217...@microsoft.com,
Nadav <Na...@discussions.microsoft.com> typed:

> Well, this is possible BUT still I would have to implement my own CAB
> extraction mechanism...
>

Here is a python script that will extract all .CAB files and hidden streams.

#
# ExtractMsiFiles.py
#
# Copyright (c) 2004, Algin Technology
# Author: Alan Klietz, al...@algintechN0SPAM.com
#
# Extract MSI files and streams
#
# Works similar to MSIEXEC.EXE /a <foo>.msi, except it also extracts
'hidden'
# installer Custom Action DLLs and hidden streams (e.g., the Authenticode
# signature stream.)
#
# To unpack the extracted .CAB files, use cabarc.exe X foo.cab
#

import pythoncom
import win32com.client
from win32com.client import constants
from win32com.server.exception import COMException

szMsiFile = ".\\UMove.msi"
szExtractDir = "."

#
# Load the "WindowsInstaller.Installer" Type Library from
#
# \python\win32all146\com\win32com\client\gen_py
# 000C1092-0000-0000-C000-000000000046x1033x1x0.py
#
# TypeLib name via OLEVIEW.EXE:
# "Microsoft Windows Installer Object Library"
#
from win32com.client import gencache
gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0)


def doit(szDatabaseFileName, method):

#
# BUG: msi.GetTypeInfo() wrongly fails instead of returning
# an ITypeInfo interface.
#
# WORKAROUND: Must specify the CLSID explicitly to coax gen_py into
# loading the typelib from the gen_py cache.
#
msi = win32com.client.Dispatch("WindowsInstaller.Installer",
resultCLSID="{000C1090-0000-0000-C000-000000000046}")

db = msi.OpenDatabase(szDatabaseFileName,
constants.msiOpenDatabaseModeReadOnly)

#
# Dig into the virtual _Streams table
#
view = db.OpenView("SELECT `Name`,`Data` FROM `_Streams`")

#
# BUG: The "Microsoft Windows Installer Object Library" typelib
# wrongly declares that Execute takes a default parameter of 0
# for the Record parameter, when it really is VT_EMPTY (None)
#
view.Execute(None) # otherwise InvokeTypes throws not-an-object on arg

while 1:
rst = view.Fetch()

if rst is None:
break

szFile = rst.StringData(1) # column 1 = Name

#
# pseudoStreams are prefixed with a \005 byte.
#
# Skip SummaryInformation -- it is actually a translation
# of the OLE property table and is not a stream.
#
# Useful for extracting DigitalSignature
#
if szFile[0:1] == chr(5):
szFile=szFile[1:] # skip leading \005 byte
if szFile.find("Summary") >= 0:
print "Skipping the pseudo-stream \"%s\"." % (szFile[1:],)
continue

print "Extracting the stream \"%s\"." % (szFile,)

f = open(szExtractDir + "\\" + szFile, "wb")

while 1:

col = 2
buflen = 4096

usz = rst.ReadStream(col, buflen, constants.msiReadStreamBytes)

if usz == 'None':
break # EOF

f.write(usz)

f.close()


del rst, view

#
# Now print the file name-to-cab-item mappings from the File table
#
view = db.OpenView("SELECT `File`, `FileName`, `Version`, `FileSize` FROM
`File`")
view.Execute(None)

szCatalog = szExtractDir + "\\FILELIST.TXT"

f = open(szCatalog, "w")

while 1:
rst = view.Fetch()

if rst is None:
break

szCabItem = rst.StringData(1)
szFile = rst.StringData(2)
szVersion = rst.StringData(3)
szSize = rst.StringData(4)

#
# Change "FOO.TXT|foo.txt" to just "foo.txt"
#
pos = szFile.find("|")
if pos >= 0:
szFile = szFile[pos+1:]

szLine = "Cab item %s: \"%s\"" % (szCabItem, szFile)

if len(szVersion) > 0:
szLine = szLine + " version %s" % (szVersion,)

szLine = szLine + ", %s bytes.\n" % (szSize,)

f.write(szLine)

f = open(szCatalog, "r")
print f.read()
f.close()

del rst, view
del db

def ExtractMsiFile():

doit(szMsiFile, 1)

print 'Done.'


ExtractMsiFile()


0 new messages