(root)/
Python-3.11.7/
PC/
_msi.c
       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 *
      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 *
      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  
      86  static FNFCIALLOC(cb_alloc)
      87  {
      88      return PyMem_RawMalloc(cb);
      89  }
      90  
      91  static FNFCIFREE(cb_free)
      92  {
      93      PyMem_RawFree(memory);
      94  }
      95  
      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  
     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  
     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  
     125  static FNFCICLOSE(cb_close)
     126  {
     127      int result = _close((int)hf);
     128      if (result != 0)
     129          *err = errno;
     130      return result;
     131  }
     132  
     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  
     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  
     154  static FNFCIFILEPLACED(cb_fileplaced)
     155  {
     156      return 0;
     157  }
     158  
     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  
     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  
     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  
     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 *
     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
     350  msiobj_dealloc(msiobj* msidb)
     351  {
     352      MsiCloseHandle(msidb->h);
     353      msidb->h = 0;
     354      PyObject_Free(msidb);
     355  }
     356  
     357  static PyObject*
     358  msierror(int status)
     359  {
     360      int code;
     361      char buf[2000];
     362      char *res = buf;
     363      DWORD size = Py_ARRAY_LENGTH(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 *
     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 *
     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 *
     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 *
     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 = Py_ARRAY_LENGTH(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 *
     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 *
     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 *
     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 *
     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*
     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 *
     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 *
     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 *
     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 *
     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 *
     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 *
     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 *
     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 *
     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 *
     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 *
    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 *
    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 *
    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 *
    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 *
    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
    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  }