blob: 01516e85ccff30b4839ca111b48bb417dbd5e057 [file] [log] [blame]
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001/* Helper library for MSI creation with Python.
Florent Xiclunac934f322010-09-03 23:47:32 +00002 * Copyright (C) 2005 Martin v. Löwis
Martin v. Löwisdd860ca2006-03-05 13:39:10 +00003 * Licensed to PSF under a contributor agreement.
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00004 */
5
6#include <Python.h>
7#include <fci.h>
8#include <fcntl.h>
9#include <windows.h>
10#include <msi.h>
11#include <msiquery.h>
12#include <msidefs.h>
13#include <rpc.h>
14
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +030015/*[clinic input]
16module _msi
17class _msi.Record "msiobj *" "&record_Type"
18class _msi.SummaryInformation "msiobj *" "&summary_Type"
19class _msi.View "msiobj *" "&msiview_Type"
20class _msi.Database "msiobj *" "&msidb_Type"
21[clinic start generated code]*/
22/*[clinic end generated code: output=da39a3ee5e6b4b0d input=89a3605762cf4bdc]*/
23
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000024static PyObject *MSIError;
25
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +030026/*[clinic input]
27_msi.UuidCreate
28
29Return the string representation of a new unique identifier.
30[clinic start generated code]*/
31
32static PyObject *
33_msi_UuidCreate_impl(PyObject *module)
34/*[clinic end generated code: output=534ecf36f10af98e input=168024ab4b3e832b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000035{
36 UUID result;
Victor Stinner9d3b93b2011-11-22 02:27:30 +010037 wchar_t *cresult;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000038 PyObject *oresult;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000039
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000040 /* May return ok, local only, and no address.
41 For local only, the documentation says we still get a uuid.
42 For RPC_S_UUID_NO_ADDRESS, it's not clear whether we can
43 use the result. */
44 if (UuidCreate(&result) == RPC_S_UUID_NO_ADDRESS) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000045 PyErr_SetString(PyExc_NotImplementedError, "processing 'no address' result");
46 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000047 }
48
Martin v. Löwis371bb502008-08-16 13:02:57 +000049 if (UuidToStringW(&result, &cresult) == RPC_S_OUT_OF_MEMORY) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000050 PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen");
51 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000052 }
53
Victor Stinner9d3b93b2011-11-22 02:27:30 +010054 oresult = PyUnicode_FromWideChar(cresult, wcslen(cresult));
Martin v. Löwis371bb502008-08-16 13:02:57 +000055 RpcStringFreeW(&cresult);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000056 return oresult;
57
58}
59
Serhiy Storchakaba67d732020-06-30 11:56:03 +030060/* Helper for converting file names from UTF-8 to wchat_t*. */
61static wchar_t *
62utf8_to_wchar(const char *s, int *err)
63{
64 PyObject *obj = PyUnicode_FromString(s);
65 if (obj == NULL) {
66 if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
67 *err = ENOMEM;
68 }
69 else {
70 *err = EINVAL;
71 }
72 PyErr_Clear();
73 return NULL;
74 }
75 wchar_t *ws = PyUnicode_AsWideCharString(obj, NULL);
76 if (ws == NULL) {
77 *err = ENOMEM;
78 PyErr_Clear();
79 }
80 Py_DECREF(obj);
81 return ws;
82}
83
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000084/* FCI callback functions */
85
86static FNFCIALLOC(cb_alloc)
87{
Serhiy Storchakaba67d732020-06-30 11:56:03 +030088 return PyMem_RawMalloc(cb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000089}
90
91static FNFCIFREE(cb_free)
92{
Serhiy Storchakaba67d732020-06-30 11:56:03 +030093 PyMem_RawFree(memory);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000094}
95
96static FNFCIOPEN(cb_open)
97{
Serhiy Storchakaba67d732020-06-30 11:56:03 +030098 wchar_t *ws = utf8_to_wchar(pszFile, err);
99 if (ws == NULL) {
100 return -1;
101 }
102 int result = _wopen(ws, oflag | O_NOINHERIT, pmode);
103 PyMem_Free(ws);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000104 if (result == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000105 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000106 return result;
107}
108
109static FNFCIREAD(cb_read)
110{
Victor Stinner67158282013-11-20 00:14:49 +0100111 UINT result = (UINT)_read((int)hf, memory, cb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000112 if (result != cb)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000113 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000114 return result;
115}
116
117static FNFCIWRITE(cb_write)
118{
Victor Stinner67158282013-11-20 00:14:49 +0100119 UINT result = (UINT)_write((int)hf, memory, cb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000120 if (result != cb)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000121 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000122 return result;
123}
124
125static FNFCICLOSE(cb_close)
126{
Victor Stinner67158282013-11-20 00:14:49 +0100127 int result = _close((int)hf);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000128 if (result != 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000129 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000130 return result;
131}
132
133static FNFCISEEK(cb_seek)
134{
Victor Stinner67158282013-11-20 00:14:49 +0100135 long result = (long)_lseek((int)hf, dist, seektype);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000136 if (result == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000137 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000138 return result;
139}
140
141static FNFCIDELETE(cb_delete)
142{
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300143 wchar_t *ws = utf8_to_wchar(pszFile, err);
144 if (ws == NULL) {
145 return -1;
146 }
147 int result = _wremove(ws);
148 PyMem_Free(ws);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000149 if (result != 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000150 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000151 return result;
152}
153
154static FNFCIFILEPLACED(cb_fileplaced)
155{
156 return 0;
157}
158
159static FNFCIGETTEMPFILE(cb_gettempfile)
160{
161 char *name = _tempnam("", "tmp");
162 if ((name != NULL) && ((int)strlen(name) < cbTempName)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000163 strcpy(pszTempName, name);
164 free(name);
165 return TRUE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000166 }
167
168 if (name) free(name);
169 return FALSE;
170}
171
172static FNFCISTATUS(cb_status)
173{
174 if (pv) {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200175 _Py_IDENTIFIER(status);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200176
177 PyObject *result = _PyObject_CallMethodId(pv, &PyId_status, "iii", typeStatus, cb1, cb2);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000178 if (result == NULL)
179 return -1;
180 Py_DECREF(result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000181 }
182 return 0;
183}
184
185static FNFCIGETNEXTCABINET(cb_getnextcabinet)
186{
187 if (pv) {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200188 _Py_IDENTIFIER(getnextcabinet);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200189
190 PyObject *result = _PyObject_CallMethodId(pv, &PyId_getnextcabinet, "i", pccab->iCab);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000191 if (result == NULL)
192 return -1;
193 if (!PyBytes_Check(result)) {
194 PyErr_Format(PyExc_TypeError,
195 "Incorrect return type %s from getnextcabinet",
Victor Stinner8182cc22020-07-10 12:40:38 +0200196 Py_TYPE(result)->tp_name);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000197 Py_DECREF(result);
198 return FALSE;
199 }
200 strncpy(pccab->szCab, PyBytes_AsString(result), sizeof(pccab->szCab));
201 return TRUE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000202 }
203 return FALSE;
204}
205
206static FNFCIGETOPENINFO(cb_getopeninfo)
207{
208 BY_HANDLE_FILE_INFORMATION bhfi;
209 FILETIME filetime;
210 HANDLE handle;
211
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300212 wchar_t *ws = utf8_to_wchar(pszName, err);
213 if (ws == NULL) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000214 return -1;
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300215 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000216
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300217 /* Need Win32 handle to get time stamps */
218 handle = CreateFileW(ws, GENERIC_READ, FILE_SHARE_READ, NULL,
219 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
220 if (handle == INVALID_HANDLE_VALUE) {
221 PyMem_Free(ws);
222 return -1;
223 }
224
225 if (GetFileInformationByHandle(handle, &bhfi) == FALSE) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000226 CloseHandle(handle);
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300227 PyMem_Free(ws);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000228 return -1;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000229 }
230
231 FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime);
232 FileTimeToDosDateTime(&filetime, pdate, ptime);
233
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000234 *pattribs = (int)(bhfi.dwFileAttributes &
235 (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH));
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000236
237 CloseHandle(handle);
238
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300239 int result = _wopen(ws, _O_RDONLY | _O_BINARY | O_NOINHERIT);
240 PyMem_Free(ws);
241 return result;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000242}
243
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300244/*[clinic input]
245_msi.FCICreate
246 cabname: str
247 the name of the CAB file
248 files: object
249 a list of tuples, each containing the name of the file on disk,
250 and the name of the file inside the CAB file
251 /
252
253Create a new CAB file.
254[clinic start generated code]*/
255
256static PyObject *
257_msi_FCICreate_impl(PyObject *module, const char *cabname, PyObject *files)
258/*[clinic end generated code: output=55dc05728361b799 input=1d2d75fdc8b44b71]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000259{
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300260 const char *p;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000261 CCAB ccab;
262 HFCI hfci;
263 ERF erf;
Christian Heimes0bd4e112008-02-12 22:59:25 +0000264 Py_ssize_t i;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000265
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000266 if (!PyList_Check(files)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000267 PyErr_SetString(PyExc_TypeError, "FCICreate expects a list");
268 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000269 }
270
271 ccab.cb = INT_MAX; /* no need to split CAB into multiple media */
272 ccab.cbFolderThresh = 1000000; /* flush directory after this many bytes */
273 ccab.cbReserveCFData = 0;
274 ccab.cbReserveCFFolder = 0;
275 ccab.cbReserveCFHeader = 0;
276
277 ccab.iCab = 1;
278 ccab.iDisk = 1;
279
280 ccab.setID = 0;
281 ccab.szDisk[0] = '\0';
282
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300283 for (i = 0, p = cabname; *p; p++)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000284 if (*p == '\\' || *p == '/')
285 i = p - cabname + 1;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000286
Christian Heimes0bd4e112008-02-12 22:59:25 +0000287 if (i >= sizeof(ccab.szCabPath) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000288 strlen(cabname+i) >= sizeof(ccab.szCab)) {
289 PyErr_SetString(PyExc_ValueError, "path name too long");
290 return 0;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000291 }
292
Christian Heimes0bd4e112008-02-12 22:59:25 +0000293 if (i > 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000294 memcpy(ccab.szCabPath, cabname, i);
295 ccab.szCabPath[i] = '\0';
296 strcpy(ccab.szCab, cabname+i);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000297 } else {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000298 strcpy(ccab.szCabPath, ".\\");
299 strcpy(ccab.szCab, cabname);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000300 }
301
302 hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000303 cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete,
304 cb_gettempfile, &ccab, NULL);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000305
306 if (hfci == NULL) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000307 PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper);
308 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000309 }
310
311 for (i=0; i < PyList_GET_SIZE(files); i++) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000312 PyObject *item = PyList_GET_ITEM(files, i);
313 char *filename, *cabname;
Zachary Ware0a29e892015-05-18 00:47:15 -0500314
315 if (!PyArg_ParseTuple(item, "ss", &filename, &cabname)) {
316 PyErr_SetString(PyExc_TypeError, "FCICreate expects a list of tuples containing two strings");
317 FCIDestroy(hfci);
318 return NULL;
319 }
320
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000321 if (!FCIAddFile(hfci, filename, cabname, FALSE,
322 cb_getnextcabinet, cb_status, cb_getopeninfo,
323 tcompTYPE_MSZIP))
324 goto err;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000325 }
326
327 if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000328 goto err;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000329
330 if (!FCIDestroy(hfci))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000331 goto err;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000332
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200333 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000334err:
Zachary Ware0a29e892015-05-18 00:47:15 -0500335 if(erf.fError)
336 PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); /* XXX better error type */
337 else
338 PyErr_SetString(PyExc_ValueError, "FCI general error");
339
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000340 FCIDestroy(hfci);
341 return NULL;
342}
343
344typedef struct msiobj{
345 PyObject_HEAD
346 MSIHANDLE h;
347}msiobj;
348
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000349static void
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000350msiobj_dealloc(msiobj* msidb)
351{
352 MsiCloseHandle(msidb->h);
353 msidb->h = 0;
Victor Stinner32bd68c2020-12-01 10:37:39 +0100354 PyObject_Free(msidb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000355}
356
357static PyObject*
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000358msierror(int status)
359{
360 int code;
361 char buf[2000];
362 char *res = buf;
363 DWORD size = sizeof(buf);
364 MSIHANDLE err = MsiGetLastErrorRecord();
365
366 if (err == 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000367 switch(status) {
368 case ERROR_ACCESS_DENIED:
369 PyErr_SetString(MSIError, "access denied");
370 return NULL;
371 case ERROR_FUNCTION_FAILED:
372 PyErr_SetString(MSIError, "function failed");
373 return NULL;
374 case ERROR_INVALID_DATA:
375 PyErr_SetString(MSIError, "invalid data");
376 return NULL;
377 case ERROR_INVALID_HANDLE:
378 PyErr_SetString(MSIError, "invalid handle");
379 return NULL;
380 case ERROR_INVALID_STATE:
381 PyErr_SetString(MSIError, "invalid state");
382 return NULL;
383 case ERROR_INVALID_PARAMETER:
384 PyErr_SetString(MSIError, "invalid parameter");
385 return NULL;
Berker Peksag4864a612017-11-24 12:53:58 +0300386 case ERROR_OPEN_FAILED:
387 PyErr_SetString(MSIError, "open failed");
388 return NULL;
389 case ERROR_CREATE_FAILED:
390 PyErr_SetString(MSIError, "create failed");
391 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000392 default:
393 PyErr_Format(MSIError, "unknown error %x", status);
394 return NULL;
395 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000396 }
397
398 code = MsiRecordGetInteger(err, 1); /* XXX code */
399 if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000400 res = malloc(size+1);
Zackery Spytz4e519372018-09-07 16:02:56 -0600401 if (res == NULL) {
402 MsiCloseHandle(err);
403 return PyErr_NoMemory();
404 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000405 MsiFormatRecord(0, err, res, &size);
406 res[size]='\0';
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000407 }
408 MsiCloseHandle(err);
409 PyErr_SetString(MSIError, res);
410 if (res != buf)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000411 free(res);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000412 return NULL;
413}
414
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300415#include "clinic/_msi.c.h"
416
417/*[clinic input]
418_msi.Database.Close
419
420Close the database object.
421[clinic start generated code]*/
422
423static PyObject *
424_msi_Database_Close_impl(msiobj *self)
425/*[clinic end generated code: output=ddf2d7712ea804f1 input=104330ce4a486187]*/
Berker Peksaga9356542017-11-07 15:58:53 +0300426{
427 int status;
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300428 if ((status = MsiCloseHandle(self->h)) != ERROR_SUCCESS) {
Berker Peksaga9356542017-11-07 15:58:53 +0300429 return msierror(status);
430 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300431 self->h = 0;
Berker Peksaga9356542017-11-07 15:58:53 +0300432 Py_RETURN_NONE;
433}
434
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000435/*************************** Record objects **********************/
436
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300437/*[clinic input]
438_msi.Record.GetFieldCount
439
440Return the number of fields of the record.
441[clinic start generated code]*/
442
443static PyObject *
444_msi_Record_GetFieldCount_impl(msiobj *self)
445/*[clinic end generated code: output=112795079c904398 input=5fb9d4071b28897b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000446{
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300447 return PyLong_FromLong(MsiRecordGetFieldCount(self->h));
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000448}
449
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300450/*[clinic input]
451_msi.Record.GetInteger
452 field: unsigned_int(bitwise=True)
453 /
454
455Return the value of field as an integer where possible.
456[clinic start generated code]*/
457
458static PyObject *
459_msi_Record_GetInteger_impl(msiobj *self, unsigned int field)
460/*[clinic end generated code: output=7174ebb6e8ed1c79 input=d19209947e2bfe61]*/
Martin v. Löwise95593e2008-06-02 10:08:54 +0000461{
Martin v. Löwise95593e2008-06-02 10:08:54 +0000462 int status;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000463
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300464 status = MsiRecordGetInteger(self->h, field);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000465 if (status == MSI_NULL_INTEGER){
466 PyErr_SetString(MSIError, "could not convert record field to integer");
467 return NULL;
468 }
Martin v. Löwis704d8b12008-06-02 11:32:23 +0000469 return PyLong_FromLong((long) status);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000470}
471
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300472/*[clinic input]
473_msi.Record.GetString
474 field: unsigned_int(bitwise=True)
475 /
476
477Return the value of field as a string where possible.
478[clinic start generated code]*/
479
480static PyObject *
481_msi_Record_GetString_impl(msiobj *self, unsigned int field)
482/*[clinic end generated code: output=f670d1b484cfa47c input=ffa11f21450b77d8]*/
Martin v. Löwise95593e2008-06-02 10:08:54 +0000483{
Martin v. Löwise95593e2008-06-02 10:08:54 +0000484 unsigned int status;
Martin v. Löwis371bb502008-08-16 13:02:57 +0000485 WCHAR buf[2000];
486 WCHAR *res = buf;
Martin v. Löwise95593e2008-06-02 10:08:54 +0000487 DWORD size = sizeof(buf);
488 PyObject* string;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000489
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300490 status = MsiRecordGetStringW(self->h, field, res, &size);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000491 if (status == ERROR_MORE_DATA) {
Martin v. Löwis371bb502008-08-16 13:02:57 +0000492 res = (WCHAR*) malloc((size + 1)*sizeof(WCHAR));
Martin v. Löwise95593e2008-06-02 10:08:54 +0000493 if (res == NULL)
494 return PyErr_NoMemory();
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300495 status = MsiRecordGetStringW(self->h, field, res, &size);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000496 }
497 if (status != ERROR_SUCCESS)
498 return msierror((int) status);
Victor Stinner9d3b93b2011-11-22 02:27:30 +0100499 string = PyUnicode_FromWideChar(res, size);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000500 if (buf != res)
501 free(res);
502 return string;
503}
504
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300505/*[clinic input]
506_msi.Record.ClearData
507
508Set all fields of the record to 0.
509[clinic start generated code]*/
510
511static PyObject *
512_msi_Record_ClearData_impl(msiobj *self)
513/*[clinic end generated code: output=1891467214b977f4 input=2a911c95aaded102]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000514{
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300515 int status = MsiRecordClearData(self->h);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000516 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000517 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000518
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200519 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000520}
521
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300522/*[clinic input]
523_msi.Record.SetString
524 field: int
525 value: Py_UNICODE
526 /
527
528Set field to a string value.
529[clinic start generated code]*/
530
531static PyObject *
532_msi_Record_SetString_impl(msiobj *self, int field, const Py_UNICODE *value)
533/*[clinic end generated code: output=2e37505b0f11f985 input=fb8ec70a2a6148e0]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000534{
535 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000536
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300537 if ((status = MsiRecordSetStringW(self->h, field, value)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000538 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000539
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200540 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000541}
542
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300543/*[clinic input]
544_msi.Record.SetStream
545 field: int
546 value: Py_UNICODE
547 /
548
549Set field to the contents of the file named value.
550[clinic start generated code]*/
551
552static PyObject *
553_msi_Record_SetStream_impl(msiobj *self, int field, const Py_UNICODE *value)
554/*[clinic end generated code: output=442facac16913b48 input=a07aa19b865e8292]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000555{
556 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000557
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300558 if ((status = MsiRecordSetStreamW(self->h, field, value)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000559 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000560
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200561 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000562}
563
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300564/*[clinic input]
565_msi.Record.SetInteger
566 field: int
567 value: int
568 /
569
570Set field to an integer value.
571[clinic start generated code]*/
572
573static PyObject *
574_msi_Record_SetInteger_impl(msiobj *self, int field, int value)
575/*[clinic end generated code: output=669e8647775d0ce7 input=c571aa775e7e451b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000576{
577 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000578
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300579 if ((status = MsiRecordSetInteger(self->h, field, value)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000580 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000581
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200582 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000583}
584
585
586
587static PyMethodDef record_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300588 _MSI_RECORD_GETFIELDCOUNT_METHODDEF
589 _MSI_RECORD_GETINTEGER_METHODDEF
590 _MSI_RECORD_GETSTRING_METHODDEF
591 _MSI_RECORD_SETSTRING_METHODDEF
592 _MSI_RECORD_SETSTREAM_METHODDEF
593 _MSI_RECORD_SETINTEGER_METHODDEF
594 _MSI_RECORD_CLEARDATA_METHODDEF
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000595 { NULL, NULL }
596};
597
598static PyTypeObject record_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000599 PyVarObject_HEAD_INIT(NULL, 0)
600 "_msi.Record", /*tp_name*/
601 sizeof(msiobj), /*tp_basicsize*/
602 0, /*tp_itemsize*/
603 /* methods */
604 (destructor)msiobj_dealloc, /*tp_dealloc*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200605 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000606 0, /*tp_getattr*/
607 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200608 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000609 0, /*tp_repr*/
610 0, /*tp_as_number*/
611 0, /*tp_as_sequence*/
612 0, /*tp_as_mapping*/
613 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000614 0, /*tp_call*/
615 0, /*tp_str*/
616 PyObject_GenericGetAttr,/*tp_getattro*/
617 PyObject_GenericSetAttr,/*tp_setattro*/
618 0, /*tp_as_buffer*/
619 Py_TPFLAGS_DEFAULT, /*tp_flags*/
620 0, /*tp_doc*/
621 0, /*tp_traverse*/
622 0, /*tp_clear*/
623 0, /*tp_richcompare*/
624 0, /*tp_weaklistoffset*/
625 0, /*tp_iter*/
626 0, /*tp_iternext*/
627 record_methods, /*tp_methods*/
628 0, /*tp_members*/
629 0, /*tp_getset*/
630 0, /*tp_base*/
631 0, /*tp_dict*/
632 0, /*tp_descr_get*/
633 0, /*tp_descr_set*/
634 0, /*tp_dictoffset*/
635 0, /*tp_init*/
636 0, /*tp_alloc*/
637 0, /*tp_new*/
638 0, /*tp_free*/
639 0, /*tp_is_gc*/
640};
641
642static PyObject*
643record_new(MSIHANDLE h)
644{
Victor Stinner92055202020-04-08 00:38:15 +0200645 msiobj *result = PyObject_New(struct msiobj, &record_Type);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000646
647 if (!result) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000648 MsiCloseHandle(h);
649 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000650 }
651
652 result->h = h;
653 return (PyObject*)result;
654}
655
656/*************************** SummaryInformation objects **************/
657
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300658/*[clinic input]
659_msi.SummaryInformation.GetProperty
660 field: int
661 the name of the property, one of the PID_* constants
662 /
663
664Return a property of the summary.
665[clinic start generated code]*/
666
667static PyObject *
668_msi_SummaryInformation_GetProperty_impl(msiobj *self, int field)
669/*[clinic end generated code: output=f8946a33ee14f6ef input=f8dfe2c890d6cb8b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000670{
671 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000672 PyObject *result;
673 UINT type;
674 INT ival;
675 FILETIME fval;
676 char sbuf[1000];
677 char *sval = sbuf;
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800678 DWORD ssize = sizeof(sbuf);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000679
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300680 status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000681 &fval, sval, &ssize);
Thomas Wouters89f507f2006-12-13 04:49:30 +0000682 if (status == ERROR_MORE_DATA) {
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800683 ssize++;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000684 sval = malloc(ssize);
Zackery Spytz4e519372018-09-07 16:02:56 -0600685 if (sval == NULL) {
686 return PyErr_NoMemory();
687 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300688 status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000689 &fval, sval, &ssize);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000690 }
Zackery Spytz549e55a2019-05-31 18:16:20 -0600691 if (status != ERROR_SUCCESS) {
692 return msierror(status);
693 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000694
695 switch(type) {
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800696 case VT_I2:
697 case VT_I4:
698 result = PyLong_FromLong(ival);
699 break;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000700 case VT_FILETIME:
701 PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800702 result = NULL;
703 break;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000704 case VT_LPSTR:
705 result = PyBytes_FromStringAndSize(sval, ssize);
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800706 break;
Berker Peksag19fb1342017-11-24 18:11:18 +0300707 case VT_EMPTY:
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800708 Py_INCREF(Py_None);
709 result = Py_None;
710 break;
711 default:
712 PyErr_Format(PyExc_NotImplementedError, "result of type %d", type);
713 result = NULL;
714 break;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000715 }
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800716 if (sval != sbuf)
717 free(sval);
718 return result;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000719}
720
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300721/*[clinic input]
722_msi.SummaryInformation.GetPropertyCount
723
724Return the number of summary properties.
725[clinic start generated code]*/
726
727static PyObject *
728_msi_SummaryInformation_GetPropertyCount_impl(msiobj *self)
729/*[clinic end generated code: output=68e94b2aeee92b3d input=2e71e985586d82dc]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000730{
731 int status;
732 UINT result;
733
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300734 status = MsiSummaryInfoGetPropertyCount(self->h, &result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000735 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000736 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000737
Christian Heimes217cfd12007-12-02 14:31:20 +0000738 return PyLong_FromLong(result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000739}
740
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300741/*[clinic input]
742_msi.SummaryInformation.SetProperty
743 field: int
744 the name of the property, one of the PID_* constants
745 value as data: object
746 the new value of the property (integer or string)
747 /
748
749Set a property.
750[clinic start generated code]*/
751
752static PyObject *
753_msi_SummaryInformation_SetProperty_impl(msiobj *self, int field,
754 PyObject *data)
755/*[clinic end generated code: output=3d4692c8984bb675 input=f2a7811b905abbed]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000756{
757 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000758
Martin v. Löwis371bb502008-08-16 13:02:57 +0000759 if (PyUnicode_Check(data)) {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300760#if USE_UNICODE_WCHAR_CACHE
Serhiy Storchakaccdc09e2017-06-28 09:55:22 +0300761 const WCHAR *value = _PyUnicode_AsUnicode(data);
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300762#else /* USE_UNICODE_WCHAR_CACHE */
763 WCHAR *value = PyUnicode_AsWideCharString(data, NULL);
764#endif /* USE_UNICODE_WCHAR_CACHE */
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300765 if (value == NULL) {
766 return NULL;
767 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300768 status = MsiSummaryInfoSetPropertyW(self->h, field, VT_LPSTR,
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300769 0, NULL, value);
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300770#if !USE_UNICODE_WCHAR_CACHE
771 PyMem_Free(value);
772#endif /* USE_UNICODE_WCHAR_CACHE */
Martin v. Löwisd1a1d1e2007-12-04 22:10:37 +0000773 } else if (PyLong_CheckExact(data)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000774 long value = PyLong_AsLong(data);
775 if (value == -1 && PyErr_Occurred()) {
776 return NULL;
777 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300778 status = MsiSummaryInfoSetProperty(self->h, field, VT_I4,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000779 value, NULL, NULL);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000780 } else {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000781 PyErr_SetString(PyExc_TypeError, "unsupported type");
782 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000783 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000784
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000785 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000786 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000787
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200788 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000789}
790
791
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300792/*[clinic input]
793_msi.SummaryInformation.Persist
794
795Write the modified properties to the summary information stream.
796[clinic start generated code]*/
797
798static PyObject *
799_msi_SummaryInformation_Persist_impl(msiobj *self)
800/*[clinic end generated code: output=c564bd17f5e122c9 input=e3dda9d530095ef7]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000801{
802 int status;
803
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300804 status = MsiSummaryInfoPersist(self->h);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000805 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000806 return msierror(status);
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200807 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000808}
809
810static PyMethodDef summary_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300811 _MSI_SUMMARYINFORMATION_GETPROPERTY_METHODDEF
812 _MSI_SUMMARYINFORMATION_GETPROPERTYCOUNT_METHODDEF
813 _MSI_SUMMARYINFORMATION_SETPROPERTY_METHODDEF
814 _MSI_SUMMARYINFORMATION_PERSIST_METHODDEF
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000815 { NULL, NULL }
816};
817
818static PyTypeObject summary_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000819 PyVarObject_HEAD_INIT(NULL, 0)
820 "_msi.SummaryInformation", /*tp_name*/
821 sizeof(msiobj), /*tp_basicsize*/
822 0, /*tp_itemsize*/
823 /* methods */
824 (destructor)msiobj_dealloc, /*tp_dealloc*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200825 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000826 0, /*tp_getattr*/
827 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200828 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000829 0, /*tp_repr*/
830 0, /*tp_as_number*/
831 0, /*tp_as_sequence*/
832 0, /*tp_as_mapping*/
833 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000834 0, /*tp_call*/
835 0, /*tp_str*/
836 PyObject_GenericGetAttr,/*tp_getattro*/
837 PyObject_GenericSetAttr,/*tp_setattro*/
838 0, /*tp_as_buffer*/
839 Py_TPFLAGS_DEFAULT, /*tp_flags*/
840 0, /*tp_doc*/
841 0, /*tp_traverse*/
842 0, /*tp_clear*/
843 0, /*tp_richcompare*/
844 0, /*tp_weaklistoffset*/
845 0, /*tp_iter*/
846 0, /*tp_iternext*/
847 summary_methods, /*tp_methods*/
848 0, /*tp_members*/
849 0, /*tp_getset*/
850 0, /*tp_base*/
851 0, /*tp_dict*/
852 0, /*tp_descr_get*/
853 0, /*tp_descr_set*/
854 0, /*tp_dictoffset*/
855 0, /*tp_init*/
856 0, /*tp_alloc*/
857 0, /*tp_new*/
858 0, /*tp_free*/
859 0, /*tp_is_gc*/
860};
861
862/*************************** View objects **************/
863
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300864/*[clinic input]
865_msi.View.Execute
866 params as oparams: object
867 a record describing actual values of the parameter tokens
868 in the query or None
869 /
870
871Execute the SQL query of the view.
872[clinic start generated code]*/
873
874static PyObject *
875_msi_View_Execute(msiobj *self, PyObject *oparams)
876/*[clinic end generated code: output=f0f65fd2900bcb4e input=cb163a15d453348e]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000877{
878 int status;
879 MSIHANDLE params = 0;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000880
881 if (oparams != Py_None) {
Victor Stinner8182cc22020-07-10 12:40:38 +0200882 if (!Py_IS_TYPE(oparams, &record_Type)) {
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000883 PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
884 return NULL;
885 }
886 params = ((msiobj*)oparams)->h;
887 }
888
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300889 status = MsiViewExecute(self->h, params);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000890 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000891 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000892
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200893 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000894}
895
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300896/*[clinic input]
897_msi.View.Fetch
898
899Return a result record of the query.
900[clinic start generated code]*/
901
902static PyObject *
903_msi_View_Fetch_impl(msiobj *self)
904/*[clinic end generated code: output=ba154a3794537d4e input=7f3e3d06c449001c]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000905{
906 int status;
907 MSIHANDLE result;
908
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300909 status = MsiViewFetch(self->h, &result);
Berker Peksagbdb83152017-11-23 15:47:30 +0300910 if (status == ERROR_NO_MORE_ITEMS) {
911 Py_RETURN_NONE;
912 } else if (status != ERROR_SUCCESS) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000913 return msierror(status);
Berker Peksagbdb83152017-11-23 15:47:30 +0300914 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000915
916 return record_new(result);
917}
918
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300919/*[clinic input]
920_msi.View.GetColumnInfo
921 kind: int
922 MSICOLINFO_NAMES or MSICOLINFO_TYPES
923 /
924
925Return a record describing the columns of the view.
926[clinic start generated code]*/
927
928static PyObject *
929_msi_View_GetColumnInfo_impl(msiobj *self, int kind)
930/*[clinic end generated code: output=e7c1697db9403660 input=afedb892bf564a3b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000931{
932 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000933 MSIHANDLE result;
934
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300935 if ((status = MsiViewGetColumnInfo(self->h, kind, &result)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000936 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000937
938 return record_new(result);
939}
940
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300941/*[clinic input]
942_msi.View.Modify
943 kind: int
944 one of the MSIMODIFY_* constants
945 data: object
946 a record describing the new data
947 /
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000948
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300949Modify the view.
950[clinic start generated code]*/
951
952static PyObject *
953_msi_View_Modify_impl(msiobj *self, int kind, PyObject *data)
954/*[clinic end generated code: output=69aaf3ce8ddac0ba input=2828de22de0d47b4]*/
955{
956 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000957
Victor Stinner8182cc22020-07-10 12:40:38 +0200958 if (!Py_IS_TYPE(data, &record_Type)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000959 PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
960 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000961 }
962
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300963 if ((status = MsiViewModify(self->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000964 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000965
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200966 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000967}
968
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300969/*[clinic input]
970_msi.View.Close
971
972Close the view.
973[clinic start generated code]*/
974
975static PyObject *
976_msi_View_Close_impl(msiobj *self)
977/*[clinic end generated code: output=488f7b8645ca104a input=de6927d1308c401c]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000978{
979 int status;
980
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300981 if ((status = MsiViewClose(self->h)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000982 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000983
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200984 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000985}
986
987static PyMethodDef view_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300988 _MSI_VIEW_EXECUTE_METHODDEF
989 _MSI_VIEW_GETCOLUMNINFO_METHODDEF
990 _MSI_VIEW_FETCH_METHODDEF
991 _MSI_VIEW_MODIFY_METHODDEF
992 _MSI_VIEW_CLOSE_METHODDEF
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000993 { NULL, NULL }
994};
995
996static PyTypeObject msiview_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000997 PyVarObject_HEAD_INIT(NULL, 0)
998 "_msi.View", /*tp_name*/
999 sizeof(msiobj), /*tp_basicsize*/
1000 0, /*tp_itemsize*/
1001 /* methods */
1002 (destructor)msiobj_dealloc, /*tp_dealloc*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001003 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001004 0, /*tp_getattr*/
1005 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001006 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001007 0, /*tp_repr*/
1008 0, /*tp_as_number*/
1009 0, /*tp_as_sequence*/
1010 0, /*tp_as_mapping*/
1011 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001012 0, /*tp_call*/
1013 0, /*tp_str*/
1014 PyObject_GenericGetAttr,/*tp_getattro*/
1015 PyObject_GenericSetAttr,/*tp_setattro*/
1016 0, /*tp_as_buffer*/
1017 Py_TPFLAGS_DEFAULT, /*tp_flags*/
1018 0, /*tp_doc*/
1019 0, /*tp_traverse*/
1020 0, /*tp_clear*/
1021 0, /*tp_richcompare*/
1022 0, /*tp_weaklistoffset*/
1023 0, /*tp_iter*/
1024 0, /*tp_iternext*/
1025 view_methods, /*tp_methods*/
1026 0, /*tp_members*/
1027 0, /*tp_getset*/
1028 0, /*tp_base*/
1029 0, /*tp_dict*/
1030 0, /*tp_descr_get*/
1031 0, /*tp_descr_set*/
1032 0, /*tp_dictoffset*/
1033 0, /*tp_init*/
1034 0, /*tp_alloc*/
1035 0, /*tp_new*/
1036 0, /*tp_free*/
1037 0, /*tp_is_gc*/
1038};
1039
1040/*************************** Database objects **************/
1041
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001042/*[clinic input]
1043_msi.Database.OpenView
1044 sql: Py_UNICODE
1045 the SQL statement to execute
1046 /
1047
1048Return a view object.
1049[clinic start generated code]*/
1050
1051static PyObject *
1052_msi_Database_OpenView_impl(msiobj *self, const Py_UNICODE *sql)
1053/*[clinic end generated code: output=e712e6a11229abfd input=50f1771f37e500df]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001054{
1055 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001056 MSIHANDLE hView;
1057 msiobj *result;
1058
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001059 if ((status = MsiDatabaseOpenViewW(self->h, sql, &hView)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001060 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001061
Victor Stinner92055202020-04-08 00:38:15 +02001062 result = PyObject_New(struct msiobj, &msiview_Type);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001063 if (!result) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001064 MsiCloseHandle(hView);
1065 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001066 }
1067
1068 result->h = hView;
1069 return (PyObject*)result;
1070}
1071
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001072/*[clinic input]
1073_msi.Database.Commit
1074
1075Commit the changes pending in the current transaction.
1076[clinic start generated code]*/
1077
1078static PyObject *
1079_msi_Database_Commit_impl(msiobj *self)
1080/*[clinic end generated code: output=f33021feb8b0cdd8 input=375bb120d402266d]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001081{
1082 int status;
1083
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001084 if ((status = MsiDatabaseCommit(self->h)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001085 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001086
Serhiy Storchaka228b12e2017-01-23 09:47:21 +02001087 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001088}
1089
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001090/*[clinic input]
1091_msi.Database.GetSummaryInformation
1092 count: int
1093 the maximum number of updated values
1094 /
1095
1096Return a new summary information object.
1097[clinic start generated code]*/
1098
1099static PyObject *
1100_msi_Database_GetSummaryInformation_impl(msiobj *self, int count)
1101/*[clinic end generated code: output=781e51a4ea4da847 input=18a899ead6521735]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001102{
1103 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001104 MSIHANDLE result;
1105 msiobj *oresult;
1106
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001107 status = MsiGetSummaryInformation(self->h, NULL, count, &result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001108 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001109 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001110
Victor Stinner92055202020-04-08 00:38:15 +02001111 oresult = PyObject_New(struct msiobj, &summary_Type);
Zackery Spytzbf94cc72019-03-07 11:20:13 -07001112 if (!oresult) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001113 MsiCloseHandle(result);
1114 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001115 }
1116
1117 oresult->h = result;
1118 return (PyObject*)oresult;
1119}
1120
1121static PyMethodDef db_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001122 _MSI_DATABASE_OPENVIEW_METHODDEF
1123 _MSI_DATABASE_COMMIT_METHODDEF
1124 _MSI_DATABASE_GETSUMMARYINFORMATION_METHODDEF
1125 _MSI_DATABASE_CLOSE_METHODDEF
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001126 { NULL, NULL }
1127};
1128
1129static PyTypeObject msidb_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001130 PyVarObject_HEAD_INIT(NULL, 0)
1131 "_msi.Database", /*tp_name*/
1132 sizeof(msiobj), /*tp_basicsize*/
1133 0, /*tp_itemsize*/
1134 /* methods */
1135 (destructor)msiobj_dealloc, /*tp_dealloc*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001136 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001137 0, /*tp_getattr*/
1138 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001139 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001140 0, /*tp_repr*/
1141 0, /*tp_as_number*/
1142 0, /*tp_as_sequence*/
1143 0, /*tp_as_mapping*/
1144 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001145 0, /*tp_call*/
1146 0, /*tp_str*/
1147 PyObject_GenericGetAttr,/*tp_getattro*/
1148 PyObject_GenericSetAttr,/*tp_setattro*/
1149 0, /*tp_as_buffer*/
1150 Py_TPFLAGS_DEFAULT, /*tp_flags*/
1151 0, /*tp_doc*/
1152 0, /*tp_traverse*/
1153 0, /*tp_clear*/
1154 0, /*tp_richcompare*/
1155 0, /*tp_weaklistoffset*/
1156 0, /*tp_iter*/
1157 0, /*tp_iternext*/
1158 db_methods, /*tp_methods*/
1159 0, /*tp_members*/
1160 0, /*tp_getset*/
1161 0, /*tp_base*/
1162 0, /*tp_dict*/
1163 0, /*tp_descr_get*/
1164 0, /*tp_descr_set*/
1165 0, /*tp_dictoffset*/
1166 0, /*tp_init*/
1167 0, /*tp_alloc*/
1168 0, /*tp_new*/
1169 0, /*tp_free*/
1170 0, /*tp_is_gc*/
1171};
1172
Steve Dower6ceda632016-09-09 11:56:34 -07001173#define Py_NOT_PERSIST(x, flag) \
Segev Finer679b5662017-07-27 01:17:57 +03001174 (x != (SIZE_T)(flag) && \
1175 x != ((SIZE_T)(flag) | MSIDBOPEN_PATCHFILE))
Steve Dower6ceda632016-09-09 11:56:34 -07001176
1177#define Py_INVALID_PERSIST(x) \
1178 (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) && \
1179 Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) && \
1180 Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) && \
1181 Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) && \
1182 Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
1183
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001184/*[clinic input]
1185_msi.OpenDatabase
1186 path: Py_UNICODE
1187 the file name of the MSI file
1188 persist: int
1189 the persistence mode
1190 /
1191
1192Return a new database object.
1193[clinic start generated code]*/
1194
1195static PyObject *
1196_msi_OpenDatabase_impl(PyObject *module, const Py_UNICODE *path, int persist)
1197/*[clinic end generated code: output=d34b7202b745de05 input=1300f3b97659559b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001198{
1199 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001200 MSIHANDLE h;
1201 msiobj *result;
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001202
Steve Dower6ceda632016-09-09 11:56:34 -07001203 /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
1204 MsiOpenDatabase may treat the value as a pointer, leading to unexpected
1205 behavior. */
1206 if (Py_INVALID_PERSIST(persist))
1207 return msierror(ERROR_INVALID_PARAMETER);
Serhiy Storchaka55939b12020-06-25 11:37:12 +03001208 status = MsiOpenDatabaseW(path, (LPCWSTR)(SIZE_T)persist, &h);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001209 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001210 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001211
Victor Stinner92055202020-04-08 00:38:15 +02001212 result = PyObject_New(struct msiobj, &msidb_Type);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001213 if (!result) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001214 MsiCloseHandle(h);
1215 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001216 }
1217 result->h = h;
1218 return (PyObject*)result;
1219}
1220
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001221/*[clinic input]
1222_msi.CreateRecord
1223 count: int
1224 the number of fields of the record
1225 /
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001226
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001227Return a new record object.
1228[clinic start generated code]*/
1229
1230static PyObject *
1231_msi_CreateRecord_impl(PyObject *module, int count)
1232/*[clinic end generated code: output=0ba0a00beea3e99e input=53f17d5b5d9b077d]*/
1233{
1234 MSIHANDLE h;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001235
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001236 h = MsiCreateRecord(count);
1237 if (h == 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001238 return msierror(0);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001239
1240 return record_new(h);
1241}
1242
1243
1244static PyMethodDef msi_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001245 _MSI_UUIDCREATE_METHODDEF
1246 _MSI_FCICREATE_METHODDEF
1247 _MSI_OPENDATABASE_METHODDEF
1248 _MSI_CREATERECORD_METHODDEF
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001249 {NULL, NULL} /* sentinel */
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001250};
1251
1252static char msi_doc[] = "Documentation";
1253
Martin v. Löwis1a214512008-06-11 05:26:20 +00001254
1255static struct PyModuleDef _msimodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001256 PyModuleDef_HEAD_INIT,
1257 "_msi",
1258 msi_doc,
1259 -1,
1260 msi_methods,
1261 NULL,
1262 NULL,
1263 NULL,
1264 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001265};
1266
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001267PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001268PyInit__msi(void)
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001269{
1270 PyObject *m;
1271
Martin v. Löwis1a214512008-06-11 05:26:20 +00001272 m = PyModule_Create(&_msimodule);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001273 if (m == NULL)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001274 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001275
Segev Finer679b5662017-07-27 01:17:57 +03001276 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (long)(SIZE_T)MSIDBOPEN_CREATEDIRECT);
1277 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (long)(SIZE_T)MSIDBOPEN_CREATE);
1278 PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (long)(SIZE_T)MSIDBOPEN_DIRECT);
1279 PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (long)(SIZE_T)MSIDBOPEN_READONLY);
1280 PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (long)(SIZE_T)MSIDBOPEN_TRANSACT);
1281 PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (long)(SIZE_T)MSIDBOPEN_PATCHFILE);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001282
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02001283 PyModule_AddIntMacro(m, MSICOLINFO_NAMES);
1284 PyModule_AddIntMacro(m, MSICOLINFO_TYPES);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001285
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02001286 PyModule_AddIntMacro(m, MSIMODIFY_SEEK);
1287 PyModule_AddIntMacro(m, MSIMODIFY_REFRESH);
1288 PyModule_AddIntMacro(m, MSIMODIFY_INSERT);
1289 PyModule_AddIntMacro(m, MSIMODIFY_UPDATE);
1290 PyModule_AddIntMacro(m, MSIMODIFY_ASSIGN);
1291 PyModule_AddIntMacro(m, MSIMODIFY_REPLACE);
1292 PyModule_AddIntMacro(m, MSIMODIFY_MERGE);
1293 PyModule_AddIntMacro(m, MSIMODIFY_DELETE);
1294 PyModule_AddIntMacro(m, MSIMODIFY_INSERT_TEMPORARY);
1295 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE);
1296 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_NEW);
1297 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_FIELD);
1298 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_DELETE);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001299
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02001300 PyModule_AddIntMacro(m, PID_CODEPAGE);
1301 PyModule_AddIntMacro(m, PID_TITLE);
1302 PyModule_AddIntMacro(m, PID_SUBJECT);
1303 PyModule_AddIntMacro(m, PID_AUTHOR);
1304 PyModule_AddIntMacro(m, PID_KEYWORDS);
1305 PyModule_AddIntMacro(m, PID_COMMENTS);
1306 PyModule_AddIntMacro(m, PID_TEMPLATE);
1307 PyModule_AddIntMacro(m, PID_LASTAUTHOR);
1308 PyModule_AddIntMacro(m, PID_REVNUMBER);
1309 PyModule_AddIntMacro(m, PID_LASTPRINTED);
1310 PyModule_AddIntMacro(m, PID_CREATE_DTM);
1311 PyModule_AddIntMacro(m, PID_LASTSAVE_DTM);
1312 PyModule_AddIntMacro(m, PID_PAGECOUNT);
1313 PyModule_AddIntMacro(m, PID_WORDCOUNT);
1314 PyModule_AddIntMacro(m, PID_CHARCOUNT);
1315 PyModule_AddIntMacro(m, PID_APPNAME);
1316 PyModule_AddIntMacro(m, PID_SECURITY);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001317
1318 MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
1319 if (!MSIError)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001320 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001321 PyModule_AddObject(m, "MSIError", MSIError);
Amaury Forgeot d'Arcbf9e9662008-06-17 21:39:46 +00001322 return m;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001323}