% Systemroot% \ Installer code>. P> Si vous ouvrez un fichier MSI avec le respect de l'outil Orca et exportez toutes les tables dans les fichiers que vous aurez exactement le même jeu d'informations em> que vous avez dans MSI. Vous pouvez modifier les tuiles de texte, puis importer les fichiers. Si vous receviez un fichier MSI vide et que vous importeriez les tables, vous créerez une nouvelle configuration Windows Installer. P> Windows SDK dispose d'une liste de scripts que vous pouvez trouver dans les fichiers c: \ programme \ Microsoft Sdks \ windows \ v7.1 \ samples \ sysmgmt \ msi \ scripts code> dossier. Vous pouvez utiliser les scripts pour créer des MSI vides et importer les tables dans MSI. Vous pouvez donc utiliser les scripts au lieu de WIX. Le WIX utilise un format XML qui rend les informations de saisie plus lisibles en tant que fichiers idt (Tables exportées par ORCA) et plus facile à entretenir. P> Pour une meilleure compréhension, j'ai écrit il y a quelques années quelques petites utilitaires qui créent les petits utilitaires qui créent le MSI Fichiers sans em> Windows Installer API et qui vient d'utiliser l'API de stockage structuré COM. De plus, j'ai créé utilitaire qui décode les informations complètes em> à partir de fichiers MSI au niveau bas (également sans utilisation de Windows Installer API). Donc, je suis sûr que les fichiers MSI ne sont vraiment pas plus que je décris ci-dessus. P> Je vois que vous êtes développeur C / C ++. Si ce serait intéressant pour vous, vous pouvez jouer avec le programme C qui crée des MSI vides. P>
#include <windows.h>
#include <Msi.h>
#include <MsiQuery.h>
#pragma warning (disable: 4201)
#include <ShLwApi.h>
#pragma warning (default: 4201)
#pragma comment (lib, "Msi.lib")
#pragma comment (lib, "ShLwApi.lib")
#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0]))
#define CONST_STR_LEN(s) (ARRAY_SIZE(s) - 1)
UINT ExecuteSimpleMsiQuery (MSIHANDLE hDatabase, LPCTSTR pszQuery)
{
UINT uStatus = ERROR_INVALID_DATA;
MSIHANDLE hView = (MSIHANDLE)0;
__try {
uStatus = MsiDatabaseOpenView (hDatabase, pszQuery, &hView);
if (uStatus != NO_ERROR) __leave;
uStatus = MsiViewExecute (hView, (MSIHANDLE)0);
if (uStatus != NO_ERROR) __leave;
uStatus = MsiViewClose(hView);
}
__finally {
if (hView != (MSIHANDLE)0)
MsiCloseHandle (hView);
}
return uStatus;
}
UINT ExecuteQueryWirhTwoStringParameters (MSIHANDLE hView, LPCTSTR pszStr1, LPCTSTR pszStr2)
{
UINT uStatus = ERROR_INVALID_DATA;
MSIHANDLE hRec = (MSIHANDLE)0;
__try {
hRec = MsiCreateRecord(2);
MsiRecordSetString (hRec, 1, pszStr1);
MsiRecordSetString (hRec, 2, pszStr2);
uStatus = MsiViewExecute (hView, hRec);
// prepair for the next call of MsiViewExecute
uStatus = MsiViewClose(hView);
}
__finally {
if (hRec != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hRec);
}
return uStatus;
}
void main()
{
LPCTSTR pszMsiName = TEXT("Empty.msi");
MSIHANDLE hDatabase = (MSIHANDLE)0, hSummaryInfo = (MSIHANDLE)0, hView = (MSIHANDLE)0;
UINT uiUpdateCount;
UINT uStatus;
HANDLE hFile = INVALID_HANDLE_VALUE;
BOOL bSuccess;
char szPropertyValues[] =
"Property\tValue\r\n"
"s72\tl0\r\n"
"Property\tProperty\r\n"
"Manufacturer\tOK soft GmbH\r\n"
"ProductCode\t{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}\r\n"
"ProductLanguage\t1031\r\n"
"ProductName\tTrust to User (T2U) Service\r\n"
"ProductVersion\t1.0\r\n"
"UpgradeCode\t{EE115A5D-D05A-465F-B077-F28CCDB20ECB}\r\n";
DWORD cbNumberOfBytesWritten;
char szBuffer[128];
__try {
UINT i, cMaxProperty=0xFFFFFF;
// Create empty database. Inspite of it is empty MSI file has 2560 bytes
uStatus = MsiOpenDatabase (pszMsiName, MSIDBOPEN_CREATE, &hDatabase);
if (uStatus != NO_ERROR) __leave;
uiUpdateCount = 9;
uStatus = MsiGetSummaryInformation (hDatabase, NULL, uiUpdateCount, &hSummaryInfo);
if (uStatus != NO_ERROR) __leave;
// PID_CODEPAGE is optional
uStatus = MsiSummaryInfoSetProperty (hSummaryInfo, PID_CODEPAGE, VT_I2, 1252, NULL, NULL);
//C:\Oleg\Win32.new\MSI\CreateEmptyMsi>msiinfo C:\Oleg\Win32.new\MSI\CreateEmptyMsi\Empty.msi
//
//Class Id for the MSI storage is {000C1084-0000-0000-C000-000000000046}
//
//Error 1627. Unable to display summary information. System does not support the codepage of the Summary Information Stream (codepage = '1200')
// VT_LPSTR MUST be used and not VT_LPWSTR.
// If one use MsiSummaryInfoSetPropertyW(..., VT_LPWSTR, 9, NULL, L"string"); one receive 1629 ERROR - ERROR_DATATYPE_MISMATCH
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_TITLE, VT_LPSTR, 0, NULL, L"Installation Database");
// PIDSI_SUBJECT and PIDSI_AUTHOR are optional
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_SUBJECT, VT_LPSTR, 0, NULL, L"Trust To User (T2U) Service");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_AUTHOR, VT_LPSTR, 0, NULL, L"OK soft GmbH");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_KEYWORDS, VT_LPSTR, 0, NULL, L"Installer,MSI,Database");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_TEMPLATE, VT_LPSTR, 0, NULL, L"Intel;1033");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_REVNUMBER, VT_LPSTR, 0, NULL, L"{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}");
uStatus = MsiSummaryInfoSetProperty (hSummaryInfo, PIDSI_PAGECOUNT, VT_I4, 110, NULL, NULL);
uStatus = MsiSummaryInfoSetProperty (hSummaryInfo, PIDSI_WORDCOUNT, VT_I4, 0, NULL, NULL);
uStatus = MsiSummaryInfoPersist (hSummaryInfo);
// if we commit database here we receive 3072 large MSI file
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("CREATE TABLE `Property` (`Property` CHAR(72) NOT NULL, `Value` CHAR(0) NOT NULL LOCALIZABLE PRIMARY KEY `Property`)"));
if (uStatus != NO_ERROR) __leave;
uStatus = MsiDatabaseOpenView (hDatabase, TEXT("INSERT INTO `Property` (`Property`, `Value`) VALUES (?, ?)"), &hView);
if (uStatus != NO_ERROR) __leave;
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("Manufacturer"), TEXT("OK soft GmbH"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductLanguage"), TEXT("1033"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductVersion"), TEXT("1.0"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductCode"), TEXT("{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductName"), TEXT("Trust to User (T2U) Service"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("UpgradeCode"), TEXT("{EE115A5D-D05A-465F-B077-F28CCDB20ECB}"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("CREATE TABLE `_Validation` (`Table` CHAR(32) NOT NULL, `Column` CHAR(32) NOT NULL, `Nullable` CHAR(4) NOT NULL, `MinValue` LONG, `MaxValue` LONG, `KeyTable` CHAR(255), `KeyColumn` INT, `Category` CHAR(32), `Set` CHAR(255), `Description` CHAR(255) PRIMARY KEY `Table`, `Column`)"));
if (uStatus != NO_ERROR) __leave;
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Category', 'Y', NULL, NULL, NULL, NULL, NULL, 'Text;Formatted;Template;Condition;Guid;Path;Version;Language;Identifier;Binary;UpperCase;LowerCase;Filename;Paths;AnyPath;WildCardFilename;RegPath;KeyFormatted;CustomSource;Property;Cabinet;Shortcut;URL', 'String category')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Column', 'N', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'Name of column')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Description', 'Y', NULL, NULL, NULL, NULL, 'Text', NULL, 'Description of column')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'KeyColumn', 'Y', 1, 32, NULL, NULL, NULL, NULL, 'Column to which foreign key connects')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'KeyTable', 'Y', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'For foreign key, Name of table to which data must link')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'MaxValue', 'Y', -2147483647, 2147483647, NULL, NULL, NULL, NULL, 'Maximum value allowed')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'MinValue', 'Y', -2147483647, 2147483647, NULL, NULL, NULL, NULL, 'Minimum value allowed')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Nullable', 'N', NULL, NULL, NULL, NULL, NULL, 'Y;N;@', 'Whether the column is nullable')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Set', 'Y', NULL, NULL, NULL, NULL, 'Text', NULL, 'Set of values that are permitted')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Table', 'N', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'Name of table')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'Property', 'Property', 'N', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'Name of property, uppercase if settable by launcher or loader.')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'Property', 'Value', 'N', NULL, NULL, NULL, NULL, 'Text', NULL, 'String value for property. Never null or empty.')"));
uStatus = MsiDatabaseCommit (hDatabase);
// now we have MSI file which has 4608 Bytes 2560 -> it is 2048 bytes larger as an empty MSI
}
__finally {
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle (hFile);
if (hView != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hView);
if (hSummaryInfo != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hSummaryInfo);
if (hDatabase != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hDatabase);
}
}
Mme ne documente pas le format MSI, comment cela fonctionne et ce qu'il fait?
La documentation de Microsoft pour Windows Installer est terrible. Ce n'est pas bien organisé et il semble être écrit pour un public qui sache déjà ce qu'il fait et comment cela le fait. Si vous voulez le comprendre, je vous recommanderais de jouer avec WIX et ORCA, car WIX est principalement une mince abstraction sur le format de base de données MSI, et l'ORCA vous permettra de voir ce qui est généré pour divers entrées WIX.
L'article Wikipedia sur le sujet est très bien écrit et répond à toutes vos questions.
Je suis d'accord sur le Doco SDK. Il m'a fallu 3 mois de voler sur des avions sans connexion Internet et / ou de distractions pour l'étudier encore et encore jusqu'à ce que de petits nucides de la connaissance me soient clairs. Quelque chose ne fonctionnerait pas, je pourrais le faire travailler puis après que l'article Doco ait du sens pourquoi cela n'a pas fonctionné.