Posts tagged with 'ctypes'

The new Ubuntu One Windows client is very close to be released (we have already been sending the new code to our beta testers) and in order to make life easier to new user we wanted to provide a migration script that will allow the user migrate his data to the new client and uninstall the old one. In order to be able to know if the old msi is present in the system we had to use the Windows Installer SDK to query the installed applications and find if the old Ubuntu One client is present.

The following code is a small script that contains the functions to query the installed software in the system which is very similar to the script found in WiLstPrd.vbs but using python instead of VB and ctypes.

# This scripts allows to get a list of all installed products in a windows# machine. The code uses ctypes becuase there were a number of issues when# trying to achieve the same win win32com.clientfromcollectionsimport namedtuple
from ctypes import byref, create_unicode_buffer, windll
from ctypes.wintypesimport DWORD
fromitertoolsimport count
# defined at http://msdn.microsoft.com/en-us/library/aa370101(v=VS.85).aspx
UID_BUFFER_SIZE = 39
PROPERTY_BUFFER_SIZE = 256
ERROR_MORE_DATA = 234
ERROR_INVALID_PARAMETER = 87
ERROR_SUCCESS = 0
ERROR_NO_MORE_ITEMS = 259
ERROR_UNKNOWN_PRODUCT = 1605# diff propoerties of a product, not all products have all properties
PRODUCT_PROPERTIES = [u'Language',
u'ProductName',
u'PackageCode',
u'Transforms',
u'AssignmentType',
u'PackageName',
u'InstalledProductName',
u'VersionString',
u'RegCompany',
u'RegOwner',
u'ProductID',
u'ProductIcon',
u'InstallLocation',
u'InstallSource',
u'InstallDate',
u'Publisher',
u'LocalPackage',
u'HelpLink',
u'HelpTelephone',
u'URLInfoAbout',
u'URLUpdateInfo',]# class to be used for python users :)
Product = namedtuple('Product', PRODUCT_PROPERTIES)def get_property_for_product(product, property, buf_size=PROPERTY_BUFFER_SIZE):
"""Retruns the value of a fiven property from a product."""
property_buffer = create_unicode_buffer(buf_size)
size = DWORD(buf_size)
result = windll.msi.MsiGetProductInfoW(product, property, property_buffer,
byref(size))if result == ERROR_MORE_DATA:
return get_property_for_product(product, property,
2* buf_size)elif result == ERROR_SUCCESS:
return property_buffer.valueelse:
returnNonedef populate_product(uid):
"""Return a Product with the different present data."""
properties = []forpropertyin PRODUCT_PROPERTIES:
properties.append(get_property_for_product(uid, property))return Product(*properties)def get_installed_products_uids():
"""Returns a list with all the different uid of the installed apps."""# enum will return an error code according to the result of the app
products = []for i in count(0):
uid_buffer = create_unicode_buffer(UID_BUFFER_SIZE)
result = windll.msi.MsiEnumProductsW(i, uid_buffer)if result == ERROR_NO_MORE_ITEMS:
# done interating over the collectionbreak
products.append(uid_buffer.value)return products
def get_installed_products():
"""Returns a collection of products that are installed in the system."""
products = []for puid in get_installed_products_uids():
products.append(populate_product(puid))return products
def is_product_installed_uid(uid):
"""Return if a product with the given id is installed.
uid Most be a unicode object with the uid of the product using
the following format {uid}
"""# we try to get the VersisonString for the uid, if we get an error it means# that the product is not installed in the system.
buf_size = 256
uid_buffer = create_unicode_buffer(uid)property = u'VersionString'
property_buffer = create_unicode_buffer(buf_size)
size = DWORD(buf_size)
result = windll.msi.MsiGetProductInfoW(uid_buffer, property, property_buffer,
byref(size))if result == ERROR_UNKNOWN_PRODUCT:
returnFalseelse:
returnTrue

The above code will allow a python developer to check which products are installed on Windows as well as to know if a product with the given UID is indeed installed.

Sometimes the Moirae (lovely three women, aren’t they?) decide that your project is going to have a complicated live, and this is what I have been facing so far with the port of Ubuntu One to Windows. This means that things that I do not anticipate to go wrong will go wrong. As an example of this is what has currently broken the nightlies of Ubuntu One on any platform (at least we have the same features in all platforms now ). The issue has happened due to some changed that added in Ubuntu SSO Client that would allow to use pykeyring on windows and the COM to detect network changes.

In Ubuntu SSO Client there was an error in the setup.py that would have the following trace

Well that is little odd, isn’t it? Why would Distutils-extra have an issue with wintypes, shouldn’t it just return an error to the stderr and leave it like that?. Well interestingly enough, the following returns a ValueError on Linux:

import ctypes.wintypes

He, interesting (I can assure you I was not this polite when I saw the error). So why is distutils extra raising this? Well the main reason resides in the __add_imports method in distutils extra that uses the ast module to find all the modules that you import and tries to import them to see if they are in the system. All of this is wrap by a try statement, but unfortunately the except clause looks for the common exceptions for error hen importing, and ValueError is not one of them. I have sent a patch to disutils-extra to work around this and sent a mail to python-dev asking where is the best place to submit a patch for ctypes…. Who said this project would not help open-source?