windows-nt/Source/XPSP1/NT/windows/appcompat/sdbapi/doc/testmsi.c
2020-09-26 16:20:57 +08:00

480 lines
16 KiB
C

/*
*
*
* This is the file you need to include to pull all the relevant
* functionality for Sdb* functions
* Note that despite api parameters appearing as TCHAR in definitions, they are always
* WCHARs on NT and always CHARs on Win9x
*
*/
#include "shimdb.h"
/*++
XML how-to
1. db.xml in xml directory is a snapshot of our actual xml file
(you should enlist into lab6 windows\published and windows\appcompat!!!)
2. Transforms(remedies) are specified under <LIBRARY> :
<!-- MSI Transforms -->
<!-- this is the file containing the transform -- it should be accessible to the compiler
when appcompat database is being built -->
<FILE NAME="notepad.exe"/>
<!-- this is description stub, transform is referenced by name ExperimentalTransform
refers to the filename notepad.exe -->
<MSI_TRANSFORM NAME="ExperimentalTransform" FILE="notepad.exe">
<DESCRIPTION>
This is a sample transform for MSI implementation
</DESCRIPTION>
</MSI_TRANSFORM>
3. Packages are specified like this:
<APP NAME="Some random app" VENDOR="ArtsCraftsAndCalamitiesInc">
<DESCRIPTION>
This is description
</DESCRIPTION>
...
<MSI_PACKAGE NAME="Test install 1" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
<DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x12345678"/>
<DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is my test string"/>
<DATA NAME="Kozu44" VALUETYPE="BINARY">
01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
</DATA>
<MSI_TRANSFORM NAME="ExperimentalTransform"/>
</MSI_PACKAGE>
<MSI_PACKAGE NAME="Test Install 2 same guid" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
<DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x45678"/>
<DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is another string"/>
<DATA NAME="Kozu44" VALUETYPE="BINARY">
01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
</DATA>
<MSI_TRANSFORM NAME="ExperimentalTransform2"/>
</MSI_PACKAGE>
</APP>
4. Compiling database - the best way to do it -- is to build sysmain.sdb using
%sdxroot%\windows\appcompat\package
to do it:
a. Enlist into lab6 windows\appcompat and windows\published,
build in windows\published then in windows\appcompat
b. modify windows\appcompat\db\db.xml
c. Copy all the msi transform files into %_NTTREE%\shimdll
c. build in windows\appcompat\package
d. appfix.exe package is ready for deployment in obj\i386
======================================================================================================
New Features:
1. Nested DATA tags:
<MSI_PACKAGE NAME="Test install 1" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
<DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x12345678"/>
<DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is my test string"/>
<DATA NAME="Kozu44" VALUETYPE="BINARY">
01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
</DATA>
<DATA NAME="Root" VALUETYPE="STRING" VALUE="ROOT">
<DATA NAME="Node1" VALUETYPE="DWORD" VALUE="0x1"/>
<DATA NAME="Node2" VALUETYPE="DWORD" VALUE="0x2">
<DATA NAME="SubNode1" VALUETYPE="DWORD" VALUE="0x3"/>
<DATA NAME="SubNode2" VALUETYPE="DWORD" VALUE="0x4"/>
</DATA>
</DATA>
<MSI_TRANSFORM NAME="ExperimentalTransform"/>
</MSI_PACKAGE>
2. New API and enhanced old api to query nested data tags:
- direct addressing:
Status = SdbQueryData(hSDB,
trMatch,
L"Root\Node2\Subnode2", // <<<<< direct path to the value
&dwDataType,
NULL,
&dwDataSize);
- via enumeration
Status = SdbQueryDataEx(hSDB,
trMatch,
L"Root",
&dwDataType,
NULL,
&dwDataSize,
&trDataRoot);
the call above retrieves trDataRoot which allows for further calls to SdbQueryData/SdbQueryDataEx:
Status = SdbQueryDataEx(hSDB,
trDataRoot,
L"Node2",
&dwDataType,
NULL,
&dwDataSize,
NULL); // <<< passing NULL is allowed
3. Fully functional custom databases.
- compile db.xml that comprises your private database
shimdbc fix -f .\MyMsiTransforms MyMsiPackages.xml MsiPrivate.sdb
(above -f parameter points shimdbc to the directory which has all the transform files)
- install the database using sdbInst (in tools directory)
sdbinst MsiPrivate.sdb
- Run query as usual -- the normal enumeration process will now include all the custom dbs in
addition to any data located in the main database
- You can unistall the db using sdbinst as well
4. New flag in SdbInitDatabase
hSDB = SdbInitDatabase(HID_DATABASE_FULLPATH|HID_DOS_PATH, L"c:\\temp\\mydb.sdb")
the flag HID_DATABASE_FULLPATH directs us to open the database specified as a second parameter and treat it as
"main" database. You can also avoid opening ANY database at all -- and rely upon local database and/or custom database:
hSDB = SdbInitDatabase(HID_NO_DB, NULL)
5. Note that NONE is a valid data type -
<DATA NAME="Foo" VALUETYPE="NONE">
</DATA>
this entry is perfectly valid
--*/
DetectMsiPackage()
{
HSDB hSDB;
SDBMSIFINDINFO MsiFindInfo;
/*++
General note:
* While debugging your code it is useful to set SHIM_DEBUG_LEVEL environment variable
* in order to see debug spew from Sdb* functions. To see all the debug output:
* Set SHIM_DEBUG_LEVEL=9
1. Initialize Database
Function:
HSDB
SdbInitDatabase(
IN DWORD dwFlags, // flags that tell how the database should be
// initialized.
IN LPCTSTR pszDatabasePath // the OPTIONAL full path to the database to
// be used.
)
dwFlags could have HID_DOS_PATH if you provide pszDatabasePath
Function will try to open sysmain.sdb and systest.sdb (if available) in \SystemRoot\AppPatch if none of the
parameters are supplied. To specify alternative location for sysmain.sdb and systest.sdb :
SdbInitDatabase(HID_DOS_PATH, L"c:\foo") will look for sysmain.sdb in c:\foo
--*/
hSDB = SdbInitDatabase(0, NULL);
if (NULL == hSDB) {
//
// failed to initialize database
//
}
/*++
2. Detect this particular package -- there could be more than one match, caller should
try and distinguish between the packages by reading supplemental data
Detection is done by calling SdbFindFirstMsiPackage/SdbFindFirsMsiPackage_Str and
SdbFindNextMsiPacakage
Functions:
TAGREF
SdbFindFirstMsiPackage_Str(
IN HSDB hSDB, // handle obtained in a call to SdbInitDatabase
IN LPCTSTR lpszGuid, // guid in the form {xxxxx.... }
IN LPCTSTR lpszLocalDB, // Optional full path to the local DB file
OUT PSDBMSIFINDINFO pFindInfo // find context
);
TAGREF
SdbFindNextMsiPackage(
IN HSDB hSDB, // handle, see above
IN OUT PSDBMSIFINDINFO pFindInfo // find context, previously obtained from SdbFindFirstMsiPackage* functions
);
--*/
trMatch = SdbFindFirstMsiPackage_Str(hSDB,
L"{740fa5d8-6472-4c36-9129-364b773fa64e}",
NULL,
&MsiFindInfo);
while (TAGREF_NULL != trMatch) {
/*++
3a.Examine name of this package. Use these functions:
WCHAR wszBuffer[MAX_PATH];
trName = SdbFindFirstTagRef(hSDB, trMatch, TAG_NAME);
if (TAGREF_NULL != trName) {
bSuccess = SdbReadStringTagRef(hSDB, trName, wszBuffer, sizeof(wszBuffer)/sizeof(wszBuffer[0]));
}
the code above reads "Special Install package" given the xml below:
<!-- MSI transform -->
<MSI_PACKAGE NAME="Special install package" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
<DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x12345678"/>
<DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is my test string"/>
<DATA NAME="Kozu44" VALUETYPE="BINARY">
01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
</DATA>
<MSI_TRANSFORM NAME="ExperimentalTransform2"/>
<MSI_TRANSFORM NAME="ExperimentalTransform3"/>
</MSI_PACKAGE>
3b.Examine supplemental data for this package:
DWORD
SdbQueryData(
IN HSDB hSDB, // database handle
IN TAGREF trExe, // tagref of the matching exe
IN LPCTSTR lpszDataName, // if this is null, will try to return all the policy names
OUT LPDWORD lpdwDataType, // pointer to data type (REG_SZ, REG_BINARY, etc)
OUT LPVOID lpBuffer, // buffer to fill with information
IN OUT LPDWORD lpdwBufferSize // pointer to buffer size
);
//
// Query for information - step 1 -- obtain all the available data for this package
//
Status = SdbQueryData(hSDB,
trMatch,
NULL, // data value name
&dwDataType, // pointer to the data type
NULL, // pointer to the buffer
&dwDataSize);
// expected return value:
// ERROR_INSUFFICIENT_BUFFER
// dwDataSize will contain the required buffer size in bytes
//
// assuming that we allocated dwDataSize bytes, pBuffer is the buffer pointer
//
Status = SdbQueryData(hSDB,
trMatch,
NULL,
&dwDataType,
pBuffer,
&dwDataSize);
//
// expected return value:
// ERROR_SUCCESS
// dwDataSize will contain the number of bytes written into the buffer
// dwDataType will contain REG_MULTI_SZ
// pBuffer will receive the following data (unicode strings, assuming xml above was used)
// Data1\0Moo2\0Kozu44\0\0
//
Status = SdbQueryData(hSDB,
trMatch,
L"Data1",
&dwDataType,
NULL,
&dwDataSize);
//
// Expected return value:
// ERROR_INSUFFICIENT_BUFFER
// dwDataSize will contain 4
// dwDataType will contain REG_DWORD
//
dwDataSize = sizeof(dwData);
Status = SdbQueryData(hSDB,
trMatch,
L"Data1",
&dwDataType,
&dwData,
&dwDataSize);
// expected return value:
// ERROR_SUCCESS
// dwData will have a value of 0x12345678
//
.... etc ...
--*/
//
// We presume that using the code above we determined that this package is a match, we break out of the loop
// trMatch is the matching package
//
if (bMatch) {
break;
}
trMatch = SdbFindNextMsiPackage(hSDB, &MsiFindInfo);
}
if (TAGREF_NULL == trMatch) {
return; // no match
}
/*++
4. Seek the remedies. To do so there are two ways:
- Enumerate all the transforms available for this package using
DWORD // returns the error code, ERROR_SUCCESS if successful
SdbEnumMsiTransforms(
IN HSDB hSDB, // db handle
IN TAGREF trMatch, // matching tagref for the package
OUT TAGREF* ptrBuffer, // pointer to the buffer that will be filled with transform tagrefs
IN OUT DWORD* pdwBufferSize // size of the buffer in bytes, upon return will be set to the number
); // of bytes written to the buffer
dwError = SdbEnumMsiTransforms(hsdb, trMatch, NULL, &dwSize);
//
// expeceted ERROR_INSUFFICIENT_BUFFER,
// allocate dwSize bytes
//
TAGREF* ptrBuffer = new TAGREF[dwSize/sizeof(TAGREF)];
dwError = SdbEnumMsiTransforms(hsdb, trMatch, ptrBuffer, &dwSize);
//
// expected return: ERROR_SUCCESS
//
// ptrBuffer will have dwSize/sizeof(TAGREF) values filled with transform tagrefs
for (i = 0; i < dwSize / sizeof(TAGREF); ++i) {
trTransform = ptrBuffer[i];
// see below what we do with trTransform
}
- Enumerate transforms one by one using macros:
SdbGetFirstMsiTransformForPackage(hSDB, trMatch)
SdbGetNextMsiTransformForPackage(hSDB, trMatch, trPrevTransform)
trTransform = SdbGetFirstMsiTransformForPackage(hSDB, trMatch);
while (trTransform != TAGREF_NULL) {
//
// see below what we do with trTansform
//
trTransform = SdbGetNextMsiTransformForPackage(hSDB, trMatch, trTransform);
}
5. Read the transforms and extract transform files if available
a. Read information:
BOOL
SdbReadMsiTransformInfo(
IN HSDB hSDB, // db handle
IN TAGREF trTransformRef, // trTransform obtained above
OUT PSDBMSITRANSFORMINFO pTransformInfo // pointer to SDBMSITRANSFORMINFO structure
);
SDBMSITRANSFORMINFO MsiTransformInfo;
//
// Members of the structure:
//
// LPCWSTR lpszTransformName; // name of the transform
// TAGREF trTransform; // tagref of this transform
// TAGREF trFile; // tagref of file for this transform (bits)
bSuccess = SdbReadMsiTransformInfo(hSDB, trTransform, &MsiTransformInfo);
if (bSuccess) {
//
// MsiTransformInfo.lpszTransformName is the name of the transform (e.g. "ExperimentalTransform2"
// from xml above. The bits are available for extraction if MsiTransformInfo.trFile != TAGREF_NULL
//
}
b. Extract Transform bits:
The structure pointed to by pTransformInfo should be obtained by calling SdbReadMsiTransformInfo
BOOL
SdbCreateMsiTransformFile(
IN HSDB hSDB, // db handle
IN LPCTSTR lpszFileName, // filename to write data to
IN PSDBMSITRANSFORMINFO pTransformInfo // pointer to the transform structure
);
bSuccess = SdbCreateMsiTransformFile(hSDB, L"c:\foo\mytransform.msi", &MsiTransformInfo);
--*/
}