• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Helper library for MSI creation with Python.
2  * Copyright (C) 2005 Martin v. Löwis
3  * Licensed to PSF under a contributor agreement.
4  */
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 
15 /*[clinic input]
16 module _msi
17 class _msi.Record "msiobj *" "&record_Type"
18 class _msi.SummaryInformation "msiobj *" "&summary_Type"
19 class _msi.View "msiobj *" "&msiview_Type"
20 class _msi.Database "msiobj *" "&msidb_Type"
21 [clinic start generated code]*/
22 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=89a3605762cf4bdc]*/
23 
24 static PyObject *MSIError;
25 
26 /*[clinic input]
27 _msi.UuidCreate
28 
29 Return the string representation of a new unique identifier.
30 [clinic start generated code]*/
31 
32 static PyObject *
_msi_UuidCreate_impl(PyObject * module)33 _msi_UuidCreate_impl(PyObject *module)
34 /*[clinic end generated code: output=534ecf36f10af98e input=168024ab4b3e832b]*/
35 {
36     UUID result;
37     wchar_t *cresult;
38     PyObject *oresult;
39 
40     /* 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) {
45         PyErr_SetString(PyExc_NotImplementedError, "processing 'no address' result");
46         return NULL;
47     }
48 
49     if (UuidToStringW(&result, &cresult) == RPC_S_OUT_OF_MEMORY) {
50         PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen");
51         return NULL;
52     }
53 
54     oresult = PyUnicode_FromWideChar(cresult, wcslen(cresult));
55     RpcStringFreeW(&cresult);
56     return oresult;
57 
58 }
59 
60 /* Helper for converting file names from UTF-8 to wchat_t*.  */
61 static wchar_t *
utf8_to_wchar(const char * s,int * err)62 utf8_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 
84 /* FCI callback functions */
85 
FNFCIALLOC(cb_alloc)86 static FNFCIALLOC(cb_alloc)
87 {
88     return PyMem_RawMalloc(cb);
89 }
90 
FNFCIFREE(cb_free)91 static FNFCIFREE(cb_free)
92 {
93     PyMem_RawFree(memory);
94 }
95 
FNFCIOPEN(cb_open)96 static FNFCIOPEN(cb_open)
97 {
98     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);
104     if (result == -1)
105         *err = errno;
106     return result;
107 }
108 
FNFCIREAD(cb_read)109 static FNFCIREAD(cb_read)
110 {
111     UINT result = (UINT)_read((int)hf, memory, cb);
112     if (result != cb)
113         *err = errno;
114     return result;
115 }
116 
FNFCIWRITE(cb_write)117 static FNFCIWRITE(cb_write)
118 {
119     UINT result = (UINT)_write((int)hf, memory, cb);
120     if (result != cb)
121         *err = errno;
122     return result;
123 }
124 
FNFCICLOSE(cb_close)125 static FNFCICLOSE(cb_close)
126 {
127     int result = _close((int)hf);
128     if (result != 0)
129         *err = errno;
130     return result;
131 }
132 
FNFCISEEK(cb_seek)133 static FNFCISEEK(cb_seek)
134 {
135     long result = (long)_lseek((int)hf, dist, seektype);
136     if (result == -1)
137         *err = errno;
138     return result;
139 }
140 
FNFCIDELETE(cb_delete)141 static FNFCIDELETE(cb_delete)
142 {
143     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);
149     if (result != 0)
150         *err = errno;
151     return result;
152 }
153 
FNFCIFILEPLACED(cb_fileplaced)154 static FNFCIFILEPLACED(cb_fileplaced)
155 {
156     return 0;
157 }
158 
FNFCIGETTEMPFILE(cb_gettempfile)159 static FNFCIGETTEMPFILE(cb_gettempfile)
160 {
161     char *name = _tempnam("", "tmp");
162     if ((name != NULL) && ((int)strlen(name) < cbTempName)) {
163         strcpy(pszTempName, name);
164         free(name);
165         return TRUE;
166     }
167 
168     if (name) free(name);
169     return FALSE;
170 }
171 
FNFCISTATUS(cb_status)172 static FNFCISTATUS(cb_status)
173 {
174     if (pv) {
175         _Py_IDENTIFIER(status);
176 
177         PyObject *result = _PyObject_CallMethodId(pv, &PyId_status, "iii", typeStatus, cb1, cb2);
178         if (result == NULL)
179             return -1;
180         Py_DECREF(result);
181     }
182     return 0;
183 }
184 
FNFCIGETNEXTCABINET(cb_getnextcabinet)185 static FNFCIGETNEXTCABINET(cb_getnextcabinet)
186 {
187     if (pv) {
188         _Py_IDENTIFIER(getnextcabinet);
189 
190         PyObject *result = _PyObject_CallMethodId(pv, &PyId_getnextcabinet, "i", pccab->iCab);
191         if (result == NULL)
192             return -1;
193         if (!PyBytes_Check(result)) {
194             PyErr_Format(PyExc_TypeError,
195                 "Incorrect return type %s from getnextcabinet",
196                 Py_TYPE(result)->tp_name);
197             Py_DECREF(result);
198             return FALSE;
199         }
200         strncpy(pccab->szCab, PyBytes_AsString(result), sizeof(pccab->szCab));
201         return TRUE;
202     }
203     return FALSE;
204 }
205 
FNFCIGETOPENINFO(cb_getopeninfo)206 static FNFCIGETOPENINFO(cb_getopeninfo)
207 {
208     BY_HANDLE_FILE_INFORMATION bhfi;
209     FILETIME filetime;
210     HANDLE handle;
211 
212     wchar_t *ws = utf8_to_wchar(pszName, err);
213     if (ws == NULL) {
214         return -1;
215     }
216 
217     /* 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) {
226         CloseHandle(handle);
227         PyMem_Free(ws);
228         return -1;
229     }
230 
231     FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime);
232     FileTimeToDosDateTime(&filetime, pdate, ptime);
233 
234     *pattribs = (int)(bhfi.dwFileAttributes &
235         (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH));
236 
237     CloseHandle(handle);
238 
239     int result = _wopen(ws, _O_RDONLY | _O_BINARY | O_NOINHERIT);
240     PyMem_Free(ws);
241     return result;
242 }
243 
244 /*[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 
253 Create a new CAB file.
254 [clinic start generated code]*/
255 
256 static PyObject *
_msi_FCICreate_impl(PyObject * module,const char * cabname,PyObject * files)257 _msi_FCICreate_impl(PyObject *module, const char *cabname, PyObject *files)
258 /*[clinic end generated code: output=55dc05728361b799 input=1d2d75fdc8b44b71]*/
259 {
260     const char *p;
261     CCAB ccab;
262     HFCI hfci;
263     ERF erf;
264     Py_ssize_t i;
265 
266     if (!PyList_Check(files)) {
267         PyErr_SetString(PyExc_TypeError, "FCICreate expects a list");
268         return NULL;
269     }
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 
283     for (i = 0, p = cabname; *p; p++)
284         if (*p == '\\' || *p == '/')
285             i = p - cabname + 1;
286 
287     if (i >= sizeof(ccab.szCabPath) ||
288         strlen(cabname+i) >= sizeof(ccab.szCab)) {
289         PyErr_SetString(PyExc_ValueError, "path name too long");
290         return 0;
291     }
292 
293     if (i > 0) {
294         memcpy(ccab.szCabPath, cabname, i);
295         ccab.szCabPath[i] = '\0';
296         strcpy(ccab.szCab, cabname+i);
297     } else {
298         strcpy(ccab.szCabPath, ".\\");
299         strcpy(ccab.szCab, cabname);
300     }
301 
302     hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free,
303         cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete,
304         cb_gettempfile, &ccab, NULL);
305 
306     if (hfci == NULL) {
307         PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper);
308         return NULL;
309     }
310 
311     for (i=0; i < PyList_GET_SIZE(files); i++) {
312         PyObject *item = PyList_GET_ITEM(files, i);
313         char *filename, *cabname;
314 
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 
321         if (!FCIAddFile(hfci, filename, cabname, FALSE,
322             cb_getnextcabinet, cb_status, cb_getopeninfo,
323             tcompTYPE_MSZIP))
324             goto err;
325     }
326 
327     if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status))
328         goto err;
329 
330     if (!FCIDestroy(hfci))
331         goto err;
332 
333     Py_RETURN_NONE;
334 err:
335     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 
340     FCIDestroy(hfci);
341     return NULL;
342 }
343 
344 typedef struct msiobj{
345     PyObject_HEAD
346     MSIHANDLE h;
347 }msiobj;
348 
349 static void
msiobj_dealloc(msiobj * msidb)350 msiobj_dealloc(msiobj* msidb)
351 {
352     MsiCloseHandle(msidb->h);
353     msidb->h = 0;
354     PyObject_Free(msidb);
355 }
356 
357 static PyObject*
msierror(int status)358 msierror(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) {
367         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;
386         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;
392         default:
393             PyErr_Format(MSIError, "unknown error %x", status);
394             return NULL;
395         }
396     }
397 
398     code = MsiRecordGetInteger(err, 1); /* XXX code */
399     if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) {
400         res = malloc(size+1);
401         if (res == NULL) {
402             MsiCloseHandle(err);
403             return PyErr_NoMemory();
404         }
405         MsiFormatRecord(0, err, res, &size);
406         res[size]='\0';
407     }
408     MsiCloseHandle(err);
409     PyErr_SetString(MSIError, res);
410     if (res != buf)
411         free(res);
412     return NULL;
413 }
414 
415 #include "clinic/_msi.c.h"
416 
417 /*[clinic input]
418 _msi.Database.Close
419 
420 Close the database object.
421 [clinic start generated code]*/
422 
423 static PyObject *
_msi_Database_Close_impl(msiobj * self)424 _msi_Database_Close_impl(msiobj *self)
425 /*[clinic end generated code: output=ddf2d7712ea804f1 input=104330ce4a486187]*/
426 {
427     int status;
428     if ((status = MsiCloseHandle(self->h)) != ERROR_SUCCESS) {
429         return msierror(status);
430     }
431     self->h = 0;
432     Py_RETURN_NONE;
433 }
434 
435 /*************************** Record objects **********************/
436 
437 /*[clinic input]
438 _msi.Record.GetFieldCount
439 
440 Return the number of fields of the record.
441 [clinic start generated code]*/
442 
443 static PyObject *
_msi_Record_GetFieldCount_impl(msiobj * self)444 _msi_Record_GetFieldCount_impl(msiobj *self)
445 /*[clinic end generated code: output=112795079c904398 input=5fb9d4071b28897b]*/
446 {
447     return PyLong_FromLong(MsiRecordGetFieldCount(self->h));
448 }
449 
450 /*[clinic input]
451 _msi.Record.GetInteger
452     field: unsigned_int(bitwise=True)
453     /
454 
455 Return the value of field as an integer where possible.
456 [clinic start generated code]*/
457 
458 static PyObject *
_msi_Record_GetInteger_impl(msiobj * self,unsigned int field)459 _msi_Record_GetInteger_impl(msiobj *self, unsigned int field)
460 /*[clinic end generated code: output=7174ebb6e8ed1c79 input=d19209947e2bfe61]*/
461 {
462     int status;
463 
464     status = MsiRecordGetInteger(self->h, field);
465     if (status == MSI_NULL_INTEGER){
466         PyErr_SetString(MSIError, "could not convert record field to integer");
467         return NULL;
468     }
469     return PyLong_FromLong((long) status);
470 }
471 
472 /*[clinic input]
473 _msi.Record.GetString
474     field: unsigned_int(bitwise=True)
475     /
476 
477 Return the value of field as a string where possible.
478 [clinic start generated code]*/
479 
480 static PyObject *
_msi_Record_GetString_impl(msiobj * self,unsigned int field)481 _msi_Record_GetString_impl(msiobj *self, unsigned int field)
482 /*[clinic end generated code: output=f670d1b484cfa47c input=ffa11f21450b77d8]*/
483 {
484     unsigned int status;
485     WCHAR buf[2000];
486     WCHAR *res = buf;
487     DWORD size = sizeof(buf);
488     PyObject* string;
489 
490     status = MsiRecordGetStringW(self->h, field, res, &size);
491     if (status == ERROR_MORE_DATA) {
492         res = (WCHAR*) malloc((size + 1)*sizeof(WCHAR));
493         if (res == NULL)
494             return PyErr_NoMemory();
495         status = MsiRecordGetStringW(self->h, field, res, &size);
496     }
497     if (status != ERROR_SUCCESS)
498         return msierror((int) status);
499     string = PyUnicode_FromWideChar(res, size);
500     if (buf != res)
501         free(res);
502     return string;
503 }
504 
505 /*[clinic input]
506 _msi.Record.ClearData
507 
508 Set all fields of the record to 0.
509 [clinic start generated code]*/
510 
511 static PyObject *
_msi_Record_ClearData_impl(msiobj * self)512 _msi_Record_ClearData_impl(msiobj *self)
513 /*[clinic end generated code: output=1891467214b977f4 input=2a911c95aaded102]*/
514 {
515     int status = MsiRecordClearData(self->h);
516     if (status != ERROR_SUCCESS)
517         return msierror(status);
518 
519     Py_RETURN_NONE;
520 }
521 
522 /*[clinic input]
523 _msi.Record.SetString
524     field: int
525     value: Py_UNICODE
526     /
527 
528 Set field to a string value.
529 [clinic start generated code]*/
530 
531 static PyObject *
_msi_Record_SetString_impl(msiobj * self,int field,const Py_UNICODE * value)532 _msi_Record_SetString_impl(msiobj *self, int field, const Py_UNICODE *value)
533 /*[clinic end generated code: output=2e37505b0f11f985 input=fb8ec70a2a6148e0]*/
534 {
535     int status;
536 
537     if ((status = MsiRecordSetStringW(self->h, field, value)) != ERROR_SUCCESS)
538         return msierror(status);
539 
540     Py_RETURN_NONE;
541 }
542 
543 /*[clinic input]
544 _msi.Record.SetStream
545     field: int
546     value: Py_UNICODE
547     /
548 
549 Set field to the contents of the file named value.
550 [clinic start generated code]*/
551 
552 static PyObject *
_msi_Record_SetStream_impl(msiobj * self,int field,const Py_UNICODE * value)553 _msi_Record_SetStream_impl(msiobj *self, int field, const Py_UNICODE *value)
554 /*[clinic end generated code: output=442facac16913b48 input=a07aa19b865e8292]*/
555 {
556     int status;
557 
558     if ((status = MsiRecordSetStreamW(self->h, field, value)) != ERROR_SUCCESS)
559         return msierror(status);
560 
561     Py_RETURN_NONE;
562 }
563 
564 /*[clinic input]
565 _msi.Record.SetInteger
566     field: int
567     value: int
568     /
569 
570 Set field to an integer value.
571 [clinic start generated code]*/
572 
573 static PyObject *
_msi_Record_SetInteger_impl(msiobj * self,int field,int value)574 _msi_Record_SetInteger_impl(msiobj *self, int field, int value)
575 /*[clinic end generated code: output=669e8647775d0ce7 input=c571aa775e7e451b]*/
576 {
577     int status;
578 
579     if ((status = MsiRecordSetInteger(self->h, field, value)) != ERROR_SUCCESS)
580         return msierror(status);
581 
582     Py_RETURN_NONE;
583 }
584 
585 
586 
587 static PyMethodDef record_methods[] = {
588     _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
595     { NULL, NULL }
596 };
597 
598 static PyTypeObject record_Type = {
599         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*/
605         0,                      /*tp_vectorcall_offset*/
606         0,                      /*tp_getattr*/
607         0,                      /*tp_setattr*/
608         0,                      /*tp_as_async*/
609         0,                      /*tp_repr*/
610         0,                      /*tp_as_number*/
611         0,                      /*tp_as_sequence*/
612         0,                      /*tp_as_mapping*/
613         0,                      /*tp_hash*/
614         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 
642 static PyObject*
record_new(MSIHANDLE h)643 record_new(MSIHANDLE h)
644 {
645     msiobj *result = PyObject_New(struct msiobj, &record_Type);
646 
647     if (!result) {
648         MsiCloseHandle(h);
649         return NULL;
650     }
651 
652     result->h = h;
653     return (PyObject*)result;
654 }
655 
656 /*************************** SummaryInformation objects **************/
657 
658 /*[clinic input]
659 _msi.SummaryInformation.GetProperty
660     field: int
661         the name of the property, one of the PID_* constants
662     /
663 
664 Return a property of the summary.
665 [clinic start generated code]*/
666 
667 static PyObject *
_msi_SummaryInformation_GetProperty_impl(msiobj * self,int field)668 _msi_SummaryInformation_GetProperty_impl(msiobj *self, int field)
669 /*[clinic end generated code: output=f8946a33ee14f6ef input=f8dfe2c890d6cb8b]*/
670 {
671     int status;
672     PyObject *result;
673     UINT type;
674     INT ival;
675     FILETIME fval;
676     char sbuf[1000];
677     char *sval = sbuf;
678     DWORD ssize = sizeof(sbuf);
679 
680     status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
681         &fval, sval, &ssize);
682     if (status == ERROR_MORE_DATA) {
683         ssize++;
684         sval = malloc(ssize);
685         if (sval == NULL) {
686             return PyErr_NoMemory();
687         }
688         status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
689             &fval, sval, &ssize);
690     }
691     if (status != ERROR_SUCCESS) {
692         return msierror(status);
693     }
694 
695     switch(type) {
696         case VT_I2:
697         case VT_I4:
698             result = PyLong_FromLong(ival);
699             break;
700         case VT_FILETIME:
701             PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
702             result = NULL;
703             break;
704         case VT_LPSTR:
705             result = PyBytes_FromStringAndSize(sval, ssize);
706             break;
707         case VT_EMPTY:
708             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;
715     }
716     if (sval != sbuf)
717         free(sval);
718     return result;
719 }
720 
721 /*[clinic input]
722 _msi.SummaryInformation.GetPropertyCount
723 
724 Return the number of summary properties.
725 [clinic start generated code]*/
726 
727 static PyObject *
_msi_SummaryInformation_GetPropertyCount_impl(msiobj * self)728 _msi_SummaryInformation_GetPropertyCount_impl(msiobj *self)
729 /*[clinic end generated code: output=68e94b2aeee92b3d input=2e71e985586d82dc]*/
730 {
731     int status;
732     UINT result;
733 
734     status = MsiSummaryInfoGetPropertyCount(self->h, &result);
735     if (status != ERROR_SUCCESS)
736         return msierror(status);
737 
738     return PyLong_FromLong(result);
739 }
740 
741 /*[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 
749 Set a property.
750 [clinic start generated code]*/
751 
752 static PyObject *
_msi_SummaryInformation_SetProperty_impl(msiobj * self,int field,PyObject * data)753 _msi_SummaryInformation_SetProperty_impl(msiobj *self, int field,
754                                          PyObject *data)
755 /*[clinic end generated code: output=3d4692c8984bb675 input=f2a7811b905abbed]*/
756 {
757     int status;
758 
759     if (PyUnicode_Check(data)) {
760 #if USE_UNICODE_WCHAR_CACHE
761         const WCHAR *value = _PyUnicode_AsUnicode(data);
762 #else /* USE_UNICODE_WCHAR_CACHE */
763         WCHAR *value = PyUnicode_AsWideCharString(data, NULL);
764 #endif /* USE_UNICODE_WCHAR_CACHE */
765         if (value == NULL) {
766             return NULL;
767         }
768         status = MsiSummaryInfoSetPropertyW(self->h, field, VT_LPSTR,
769             0, NULL, value);
770 #if !USE_UNICODE_WCHAR_CACHE
771         PyMem_Free(value);
772 #endif /* USE_UNICODE_WCHAR_CACHE */
773     } else if (PyLong_CheckExact(data)) {
774         long value = PyLong_AsLong(data);
775         if (value == -1 && PyErr_Occurred()) {
776             return NULL;
777         }
778         status = MsiSummaryInfoSetProperty(self->h, field, VT_I4,
779             value, NULL, NULL);
780     } else {
781         PyErr_SetString(PyExc_TypeError, "unsupported type");
782         return NULL;
783     }
784 
785     if (status != ERROR_SUCCESS)
786         return msierror(status);
787 
788     Py_RETURN_NONE;
789 }
790 
791 
792 /*[clinic input]
793 _msi.SummaryInformation.Persist
794 
795 Write the modified properties to the summary information stream.
796 [clinic start generated code]*/
797 
798 static PyObject *
_msi_SummaryInformation_Persist_impl(msiobj * self)799 _msi_SummaryInformation_Persist_impl(msiobj *self)
800 /*[clinic end generated code: output=c564bd17f5e122c9 input=e3dda9d530095ef7]*/
801 {
802     int status;
803 
804     status = MsiSummaryInfoPersist(self->h);
805     if (status != ERROR_SUCCESS)
806         return msierror(status);
807     Py_RETURN_NONE;
808 }
809 
810 static PyMethodDef summary_methods[] = {
811     _MSI_SUMMARYINFORMATION_GETPROPERTY_METHODDEF
812     _MSI_SUMMARYINFORMATION_GETPROPERTYCOUNT_METHODDEF
813     _MSI_SUMMARYINFORMATION_SETPROPERTY_METHODDEF
814     _MSI_SUMMARYINFORMATION_PERSIST_METHODDEF
815     { NULL, NULL }
816 };
817 
818 static PyTypeObject summary_Type = {
819         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*/
825         0,                      /*tp_vectorcall_offset*/
826         0,                      /*tp_getattr*/
827         0,                      /*tp_setattr*/
828         0,                      /*tp_as_async*/
829         0,                      /*tp_repr*/
830         0,                      /*tp_as_number*/
831         0,                      /*tp_as_sequence*/
832         0,                      /*tp_as_mapping*/
833         0,                      /*tp_hash*/
834         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 
864 /*[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 
871 Execute the SQL query of the view.
872 [clinic start generated code]*/
873 
874 static PyObject *
_msi_View_Execute(msiobj * self,PyObject * oparams)875 _msi_View_Execute(msiobj *self, PyObject *oparams)
876 /*[clinic end generated code: output=f0f65fd2900bcb4e input=cb163a15d453348e]*/
877 {
878     int status;
879     MSIHANDLE params = 0;
880 
881     if (oparams != Py_None) {
882         if (!Py_IS_TYPE(oparams, &record_Type)) {
883             PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
884             return NULL;
885         }
886         params = ((msiobj*)oparams)->h;
887     }
888 
889     status = MsiViewExecute(self->h, params);
890     if (status != ERROR_SUCCESS)
891         return msierror(status);
892 
893     Py_RETURN_NONE;
894 }
895 
896 /*[clinic input]
897 _msi.View.Fetch
898 
899 Return a result record of the query.
900 [clinic start generated code]*/
901 
902 static PyObject *
_msi_View_Fetch_impl(msiobj * self)903 _msi_View_Fetch_impl(msiobj *self)
904 /*[clinic end generated code: output=ba154a3794537d4e input=7f3e3d06c449001c]*/
905 {
906     int status;
907     MSIHANDLE result;
908 
909     status = MsiViewFetch(self->h, &result);
910     if (status == ERROR_NO_MORE_ITEMS) {
911         Py_RETURN_NONE;
912     } else if (status != ERROR_SUCCESS) {
913         return msierror(status);
914     }
915 
916     return record_new(result);
917 }
918 
919 /*[clinic input]
920 _msi.View.GetColumnInfo
921     kind: int
922         MSICOLINFO_NAMES or MSICOLINFO_TYPES
923     /
924 
925 Return a record describing the columns of the view.
926 [clinic start generated code]*/
927 
928 static PyObject *
_msi_View_GetColumnInfo_impl(msiobj * self,int kind)929 _msi_View_GetColumnInfo_impl(msiobj *self, int kind)
930 /*[clinic end generated code: output=e7c1697db9403660 input=afedb892bf564a3b]*/
931 {
932     int status;
933     MSIHANDLE result;
934 
935     if ((status = MsiViewGetColumnInfo(self->h, kind, &result)) != ERROR_SUCCESS)
936         return msierror(status);
937 
938     return record_new(result);
939 }
940 
941 /*[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     /
948 
949 Modify the view.
950 [clinic start generated code]*/
951 
952 static PyObject *
_msi_View_Modify_impl(msiobj * self,int kind,PyObject * data)953 _msi_View_Modify_impl(msiobj *self, int kind, PyObject *data)
954 /*[clinic end generated code: output=69aaf3ce8ddac0ba input=2828de22de0d47b4]*/
955 {
956     int status;
957 
958     if (!Py_IS_TYPE(data, &record_Type)) {
959         PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
960         return NULL;
961     }
962 
963     if ((status = MsiViewModify(self->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
964         return msierror(status);
965 
966     Py_RETURN_NONE;
967 }
968 
969 /*[clinic input]
970 _msi.View.Close
971 
972 Close the view.
973 [clinic start generated code]*/
974 
975 static PyObject *
_msi_View_Close_impl(msiobj * self)976 _msi_View_Close_impl(msiobj *self)
977 /*[clinic end generated code: output=488f7b8645ca104a input=de6927d1308c401c]*/
978 {
979     int status;
980 
981     if ((status = MsiViewClose(self->h)) != ERROR_SUCCESS)
982         return msierror(status);
983 
984     Py_RETURN_NONE;
985 }
986 
987 static PyMethodDef view_methods[] = {
988     _MSI_VIEW_EXECUTE_METHODDEF
989     _MSI_VIEW_GETCOLUMNINFO_METHODDEF
990     _MSI_VIEW_FETCH_METHODDEF
991     _MSI_VIEW_MODIFY_METHODDEF
992     _MSI_VIEW_CLOSE_METHODDEF
993     { NULL, NULL }
994 };
995 
996 static PyTypeObject msiview_Type = {
997         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*/
1003         0,                      /*tp_vectorcall_offset*/
1004         0,                      /*tp_getattr*/
1005         0,                      /*tp_setattr*/
1006         0,                      /*tp_as_async*/
1007         0,                      /*tp_repr*/
1008         0,                      /*tp_as_number*/
1009         0,                      /*tp_as_sequence*/
1010         0,                      /*tp_as_mapping*/
1011         0,                      /*tp_hash*/
1012         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 
1042 /*[clinic input]
1043 _msi.Database.OpenView
1044     sql: Py_UNICODE
1045         the SQL statement to execute
1046     /
1047 
1048 Return a view object.
1049 [clinic start generated code]*/
1050 
1051 static PyObject *
_msi_Database_OpenView_impl(msiobj * self,const Py_UNICODE * sql)1052 _msi_Database_OpenView_impl(msiobj *self, const Py_UNICODE *sql)
1053 /*[clinic end generated code: output=e712e6a11229abfd input=50f1771f37e500df]*/
1054 {
1055     int status;
1056     MSIHANDLE hView;
1057     msiobj *result;
1058 
1059     if ((status = MsiDatabaseOpenViewW(self->h, sql, &hView)) != ERROR_SUCCESS)
1060         return msierror(status);
1061 
1062     result = PyObject_New(struct msiobj, &msiview_Type);
1063     if (!result) {
1064         MsiCloseHandle(hView);
1065         return NULL;
1066     }
1067 
1068     result->h = hView;
1069     return (PyObject*)result;
1070 }
1071 
1072 /*[clinic input]
1073 _msi.Database.Commit
1074 
1075 Commit the changes pending in the current transaction.
1076 [clinic start generated code]*/
1077 
1078 static PyObject *
_msi_Database_Commit_impl(msiobj * self)1079 _msi_Database_Commit_impl(msiobj *self)
1080 /*[clinic end generated code: output=f33021feb8b0cdd8 input=375bb120d402266d]*/
1081 {
1082     int status;
1083 
1084     if ((status = MsiDatabaseCommit(self->h)) != ERROR_SUCCESS)
1085         return msierror(status);
1086 
1087     Py_RETURN_NONE;
1088 }
1089 
1090 /*[clinic input]
1091 _msi.Database.GetSummaryInformation
1092     count: int
1093         the maximum number of updated values
1094     /
1095 
1096 Return a new summary information object.
1097 [clinic start generated code]*/
1098 
1099 static PyObject *
_msi_Database_GetSummaryInformation_impl(msiobj * self,int count)1100 _msi_Database_GetSummaryInformation_impl(msiobj *self, int count)
1101 /*[clinic end generated code: output=781e51a4ea4da847 input=18a899ead6521735]*/
1102 {
1103     int status;
1104     MSIHANDLE result;
1105     msiobj *oresult;
1106 
1107     status = MsiGetSummaryInformation(self->h, NULL, count, &result);
1108     if (status != ERROR_SUCCESS)
1109         return msierror(status);
1110 
1111     oresult = PyObject_New(struct msiobj, &summary_Type);
1112     if (!oresult) {
1113         MsiCloseHandle(result);
1114         return NULL;
1115     }
1116 
1117     oresult->h = result;
1118     return (PyObject*)oresult;
1119 }
1120 
1121 static PyMethodDef db_methods[] = {
1122     _MSI_DATABASE_OPENVIEW_METHODDEF
1123     _MSI_DATABASE_COMMIT_METHODDEF
1124     _MSI_DATABASE_GETSUMMARYINFORMATION_METHODDEF
1125     _MSI_DATABASE_CLOSE_METHODDEF
1126     { NULL, NULL }
1127 };
1128 
1129 static PyTypeObject msidb_Type = {
1130         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*/
1136         0,                      /*tp_vectorcall_offset*/
1137         0,                      /*tp_getattr*/
1138         0,                      /*tp_setattr*/
1139         0,                      /*tp_as_async*/
1140         0,                      /*tp_repr*/
1141         0,                      /*tp_as_number*/
1142         0,                      /*tp_as_sequence*/
1143         0,                      /*tp_as_mapping*/
1144         0,                      /*tp_hash*/
1145         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 
1173 #define Py_NOT_PERSIST(x, flag)                        \
1174     (x != (SIZE_T)(flag) &&                      \
1175     x != ((SIZE_T)(flag) | MSIDBOPEN_PATCHFILE))
1176 
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 
1184 /*[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 
1192 Return a new database object.
1193 [clinic start generated code]*/
1194 
1195 static PyObject *
_msi_OpenDatabase_impl(PyObject * module,const Py_UNICODE * path,int persist)1196 _msi_OpenDatabase_impl(PyObject *module, const Py_UNICODE *path, int persist)
1197 /*[clinic end generated code: output=d34b7202b745de05 input=1300f3b97659559b]*/
1198 {
1199     int status;
1200     MSIHANDLE h;
1201     msiobj *result;
1202 
1203     /* 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);
1208     status = MsiOpenDatabaseW(path, (LPCWSTR)(SIZE_T)persist, &h);
1209     if (status != ERROR_SUCCESS)
1210         return msierror(status);
1211 
1212     result = PyObject_New(struct msiobj, &msidb_Type);
1213     if (!result) {
1214         MsiCloseHandle(h);
1215         return NULL;
1216     }
1217     result->h = h;
1218     return (PyObject*)result;
1219 }
1220 
1221 /*[clinic input]
1222 _msi.CreateRecord
1223     count: int
1224         the number of fields of the record
1225     /
1226 
1227 Return a new record object.
1228 [clinic start generated code]*/
1229 
1230 static PyObject *
_msi_CreateRecord_impl(PyObject * module,int count)1231 _msi_CreateRecord_impl(PyObject *module, int count)
1232 /*[clinic end generated code: output=0ba0a00beea3e99e input=53f17d5b5d9b077d]*/
1233 {
1234     MSIHANDLE h;
1235 
1236     h = MsiCreateRecord(count);
1237     if (h == 0)
1238         return msierror(0);
1239 
1240     return record_new(h);
1241 }
1242 
1243 
1244 static PyMethodDef msi_methods[] = {
1245         _MSI_UUIDCREATE_METHODDEF
1246         _MSI_FCICREATE_METHODDEF
1247         _MSI_OPENDATABASE_METHODDEF
1248         _MSI_CREATERECORD_METHODDEF
1249         {NULL,          NULL}           /* sentinel */
1250 };
1251 
1252 static char msi_doc[] = "Documentation";
1253 
1254 
1255 static struct PyModuleDef _msimodule = {
1256         PyModuleDef_HEAD_INIT,
1257         "_msi",
1258         msi_doc,
1259         -1,
1260         msi_methods,
1261         NULL,
1262         NULL,
1263         NULL,
1264         NULL
1265 };
1266 
1267 PyMODINIT_FUNC
PyInit__msi(void)1268 PyInit__msi(void)
1269 {
1270     PyObject *m;
1271 
1272     m = PyModule_Create(&_msimodule);
1273     if (m == NULL)
1274         return NULL;
1275 
1276     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);
1282 
1283     PyModule_AddIntMacro(m, MSICOLINFO_NAMES);
1284     PyModule_AddIntMacro(m, MSICOLINFO_TYPES);
1285 
1286     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);
1299 
1300     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);
1317 
1318     MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
1319     if (!MSIError)
1320         return NULL;
1321     PyModule_AddObject(m, "MSIError", MSIError);
1322     return m;
1323 }
1324