22#include < tchar.h>
33#include < MsiQuery.h>
44#include < iostream>
5+ #include < memory>
56
67#pragma comment(lib, "msi.lib")
78
1213#endif // _UNICODE
1314
1415void HandleWindowsError (LPCTSTR func) {
15- DWORD error = GetLastError ();
16+ DWORD error{GetLastError ()};
17+ if (error == ERROR_SUCCESS ) return ;
1618 LPTSTR errorMessage;
17- FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS , NULL , error, MAKELANGID (LANG_NEUTRAL , SUBLANG_DEFAULT ), reinterpret_cast <LPTSTR >(&errorMessage), 0 , NULL );
18- std::tcerr << TEXT (" error at " ) << func << TEXT (" : " ) << errorMessage << std::endl;
19+ FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS , nullptr , error, MAKELANGID (LANG_NEUTRAL , SUBLANG_DEFAULT ), reinterpret_cast <LPTSTR >(&errorMessage), 0u , nullptr );
20+ std::tcerr << TEXT (" Windows error at " ) << func << TEXT (" : " ) << errorMessage << TEXT ( " ( " ) << error << TEXT ( " ) " ) << std::endl;
1921 LocalFree (errorMessage);
2022}
2123
22- void HandleMsiError (LPCTSTR func) {
24+ void HandleMsiError (UINT result, LPCTSTR func) {
2325 PMSIHANDLE error = MsiGetLastErrorRecord ();
2426 if (error) {
25- DWORD length = 0 ;
26- UINT status = MsiFormatRecord (0 , error, TEXT (" " ), &length);
27+ DWORD length{ 0u } ;
28+ auto status{ MsiFormatRecord (0u , error, TEXT (" " ), &length)}; // get the length of the error message
2729 if (status == ERROR_MORE_DATA ) {
28- length++;
29- LPTSTR errorMessage = new TCHAR [length] ;
30- status = MsiFormatRecord (0 , error, errorMessage, &length);
30+ length++; // for the '\0' terminator
31+ auto errorMessage{std::make_unique< TCHAR []>(length)} ;
32+ status = MsiFormatRecord (0u , error, errorMessage. get () , &length);
3133 if (status == ERROR_SUCCESS ) {
32- std::tcerr << TEXT (" error at " ) << func << TEXT (" : " ) << errorMessage << std::endl;
34+ std::tcerr << TEXT (" MSI error at " ) << func << TEXT (" : " ) << errorMessage << TEXT ( " ( " ) << result << TEXT ( " ) " ) << std::endl;
3335 }
34- delete[] errorMessage;
3536 }
3637 }
37- else {
38+ else { // no error record, just print the error code
39+ std::tcerr << TEXT (" MSI error at " ) << func << TEXT (" (" ) << result << TEXT (" )" ) << std::endl;
3840 HandleWindowsError (func);
3941 }
4042}
@@ -46,76 +48,109 @@ int _tmain(int argc, TCHAR **argv) {
4648 return 1 ;
4749 }
4850
49- PMSIHANDLE source, target, view, record;
50- UINT uResult;
51- DWORD dwResult;
51+ PMSIHANDLE source, target, view, record, summaryInfo ;
52+ UINT uResult, dataType ;
53+ DWORD dwResult, length{ 0u } ;
5254 BOOL bResult;
5355 TCHAR tempPath[MAX_PATH ], tempFile[MAX_PATH ];
5456
5557 dwResult = GetTempPath (MAX_PATH , tempPath);
56- if (dwResult > MAX_PATH || dwResult == 0 ) {
58+ if (dwResult > MAX_PATH || dwResult == 0u ) {
5759 HandleWindowsError (TEXT (" GetTempPath" ));
5860 return 1 ;
5961 }
60- uResult = GetTempFileName (tempPath, TEXT (" CELT" ), 0 , tempFile);
62+ uResult = GetTempFileName (tempPath, TEXT (" CELT" ), 0u , tempFile);
6163 if (uResult == 0 ) {
6264 HandleWindowsError (TEXT (" GetTempFileName" ));
6365 return 1 ;
6466 }
6567
6668 uResult = MsiOpenDatabase (argv[1 ], MSIDBOPEN_TRANSACT , &target);
6769 if (uResult != ERROR_SUCCESS ) {
68- HandleMsiError (TEXT (" MsiOpenDatabase target" ));
70+ HandleMsiError (uResult, TEXT (" MsiOpenDatabase target" ));
6971 return 1 ;
7072 }
7173 uResult = MsiOpenDatabase (argv[2 ], MSIDBOPEN_READONLY , &source);
7274 if (uResult != ERROR_SUCCESS ) {
73- HandleMsiError (TEXT (" MsiOpenDatabase source" ));
75+ HandleMsiError (uResult, TEXT (" MsiOpenDatabase source" ));
7476 return 1 ;
7577 }
7678 uResult = MsiDatabaseGenerateTransform (source, target, tempFile, 0 , 0 );
7779 if (uResult != ERROR_SUCCESS ) {
78- HandleMsiError (TEXT (" MsiDatabaseGenerateTransform" ));
80+ HandleMsiError (uResult, TEXT (" MsiDatabaseGenerateTransform" ));
7981 return 1 ;
8082 }
8183 uResult = MsiCreateTransformSummaryInfo (source, target, tempFile, 0 , 0 );
8284 if (uResult != ERROR_SUCCESS ) {
83- HandleMsiError (TEXT (" MsiCreateTransformSummaryInfo" ));
85+ HandleMsiError (uResult, TEXT (" MsiCreateTransformSummaryInfo" ));
8486 return 1 ;
8587 }
8688 uResult = MsiDatabaseOpenView (target, TEXT (" SELECT `Name`,`Data` FROM _Storages" ), &view);
8789 if (uResult != ERROR_SUCCESS ) {
88- HandleMsiError (TEXT (" MsiDatabaseOpenView" ));
90+ HandleMsiError (uResult, TEXT (" MsiDatabaseOpenView" ));
8991 return 1 ;
9092 }
9193 record = MsiCreateRecord (2 );
9294 if (!record) {
9395 std::tcerr << TEXT (" error at MsiCreateRecord" ) << std::endl;
9496 return 1 ;
9597 }
96- uResult = MsiRecordSetString (record, 1 , argv[3 ]);
98+ uResult = MsiRecordSetString (record, 1u , argv[3 ]);
9799 if (uResult != ERROR_SUCCESS ) {
98- HandleMsiError (TEXT (" MsiRecordSetString substorage-name" ));
100+ HandleMsiError (uResult, TEXT (" MsiRecordSetString substorage-name" ));
99101 return 1 ;
100102 }
101103 uResult = MsiViewExecute (view, record);
102104 if (uResult != ERROR_SUCCESS ) {
103- HandleMsiError (TEXT (" MsiViewExecute" ));
105+ HandleMsiError (uResult, TEXT (" MsiViewExecute" ));
104106 return 1 ;
105107 }
106- uResult = MsiRecordSetStream (record, 2 , tempFile);
108+ uResult = MsiRecordSetStream (record, 2u , tempFile);
107109 if (uResult != ERROR_SUCCESS ) {
108- HandleMsiError (TEXT (" MsiRecordSetStream substorage-content" ));
110+ HandleMsiError (uResult, TEXT (" MsiRecordSetStream substorage-content" ));
109111 return 1 ;
110112 }
111113 uResult = MsiViewModify (view, MSIMODIFY_ASSIGN , record);
112114 if (uResult != ERROR_SUCCESS ) {
113- HandleMsiError (TEXT (" MsiViewModify" ));
115+ HandleMsiError (uResult, TEXT (" MsiViewModify" ));
116+ return 1 ;
117+ }
118+ uResult = MsiGetSummaryInformation (target, nullptr , 1u , &summaryInfo);
119+ if (uResult != ERROR_SUCCESS ) {
120+ HandleMsiError (uResult, TEXT (" MsiGetSummaryInformation" ));
121+ return 1 ;
122+ }
123+ uResult = MsiSummaryInfoGetProperty (summaryInfo, PIDSI_TEMPLATE , &dataType, nullptr , nullptr , TEXT (" " ), &length); // get the length of the stored value
124+ if (uResult != ERROR_MORE_DATA ) {
125+ HandleMsiError (uResult, TEXT (" MsiSummaryInfoGetProperty length" ));
126+ return 1 ;
127+ }
128+ length++; // for the '\0' terminator
129+ {
130+ auto stringValue{std::make_unique<TCHAR []>(length + _tcslen (argv[3 ]) + 1 )}; // allocate a buffer long enough for the new value (including the separator)
131+ uResult = MsiSummaryInfoGetProperty (summaryInfo, PIDSI_TEMPLATE , &dataType, nullptr , nullptr , stringValue.get (), &length);
132+ if (uResult != ERROR_SUCCESS ) {
133+ HandleMsiError (uResult, TEXT (" MsiSummaryInfoGetProperty value" ));
134+ return 1 ;
135+ }
136+ if (_tcsstr (stringValue.get (), argv[3 ]) == nullptr ) { // check if the required value is already included, only add it if not
137+ stringValue[length] = TEXT (' ,' );
138+ _tcscpy (stringValue.get () + length + 1 , argv[3 ]);
139+ uResult = MsiSummaryInfoSetProperty (summaryInfo, PIDSI_TEMPLATE , dataType, 0 , nullptr , stringValue.get ());
140+ if (uResult != ERROR_SUCCESS ) {
141+ HandleMsiError (uResult, TEXT (" MsiSummaryInfoSetProperty" ));
142+ return 1 ;
143+ }
144+ }
145+ }
146+ uResult = MsiSummaryInfoPersist (summaryInfo);
147+ if (uResult != ERROR_SUCCESS ) {
148+ HandleMsiError (uResult, TEXT (" MsiSummaryInfoPersist" ));
114149 return 1 ;
115150 }
116151 uResult = MsiDatabaseCommit (target);
117152 if (uResult != ERROR_SUCCESS ) {
118- HandleMsiError (TEXT (" MsiDatabaseCommit target" ));
153+ HandleMsiError (uResult, TEXT (" MsiDatabaseCommit target" ));
119154 return 1 ;
120155 }
121156
0 commit comments