+++ /dev/null
-# Microsoft Installer Library\r
-# (C) 2003 Martin v. Loewis\r
-\r
-import win32com.client.gencache\r
-import win32com.client\r
-import pythoncom, pywintypes\r
-from win32com.client import constants\r
-import re, string, os, sets, glob, subprocess, sys, _winreg, struct\r
-\r
-try:\r
- basestring\r
-except NameError:\r
- basestring = (str, unicode)\r
-\r
-# Partially taken from Wine\r
-datasizemask= 0x00ff\r
-type_valid= 0x0100\r
-type_localizable= 0x0200\r
-\r
-typemask= 0x0c00\r
-type_long= 0x0000\r
-type_short= 0x0400\r
-type_string= 0x0c00\r
-type_binary= 0x0800\r
-\r
-type_nullable= 0x1000\r
-type_key= 0x2000\r
-# XXX temporary, localizable?\r
-knownbits = datasizemask | type_valid | type_localizable | \\r
- typemask | type_nullable | type_key\r
-\r
-# Summary Info Property IDs\r
-PID_CODEPAGE=1\r
-PID_TITLE=2\r
-PID_SUBJECT=3\r
-PID_AUTHOR=4\r
-PID_KEYWORDS=5\r
-PID_COMMENTS=6\r
-PID_TEMPLATE=7\r
-PID_LASTAUTHOR=8\r
-PID_REVNUMBER=9\r
-PID_LASTPRINTED=11\r
-PID_CREATE_DTM=12\r
-PID_LASTSAVE_DTM=13\r
-PID_PAGECOUNT=14\r
-PID_WORDCOUNT=15\r
-PID_CHARCOUNT=16\r
-PID_APPNAME=18\r
-PID_SECURITY=19\r
-\r
-def reset():\r
- global _directories\r
- _directories = sets.Set()\r
-\r
-def EnsureMSI():\r
- win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0)\r
-\r
-def EnsureMSM():\r
- try:\r
- win32com.client.gencache.EnsureModule('{0ADDA82F-2C26-11D2-AD65-00A0C9AF11A6}', 0, 1, 0)\r
- except pywintypes.com_error:\r
- win32com.client.gencache.EnsureModule('{0ADDA82F-2C26-11D2-AD65-00A0C9AF11A6}', 0, 2, 0)\r
-\r
-_Installer=None\r
-def MakeInstaller():\r
- global _Installer\r
- if _Installer is None:\r
- EnsureMSI()\r
- _Installer = win32com.client.Dispatch('WindowsInstaller.Installer',\r
- resultCLSID='{000C1090-0000-0000-C000-000000000046}')\r
- return _Installer\r
-\r
-_Merge=None\r
-def MakeMerge2():\r
- global _Merge\r
- if _Merge is None:\r
- EnsureMSM()\r
- _Merge = win32com.client.Dispatch("Msm.Merge2.1")\r
- return _Merge\r
-\r
-class Table:\r
- def __init__(self, name):\r
- self.name = name\r
- self.fields = []\r
-\r
- def add_field(self, index, name, type):\r
- self.fields.append((index,name,type))\r
-\r
- def sql(self):\r
- fields = []\r
- keys = []\r
- self.fields.sort()\r
- fields = [None]*len(self.fields)\r
- for index, name, type in self.fields:\r
- index -= 1\r
- unk = type & ~knownbits\r
- if unk:\r
- print "%s.%s unknown bits %x" % (self.name, name, unk)\r
- size = type & datasizemask\r
- dtype = type & typemask\r
- if dtype == type_string:\r
- if size:\r
- tname="CHAR(%d)" % size\r
- else:\r
- tname="CHAR"\r
- elif dtype == type_short:\r
- assert size==2\r
- tname = "SHORT"\r
- elif dtype == type_long:\r
- assert size==4\r
- tname="LONG"\r
- elif dtype == type_binary:\r
- assert size==0\r
- tname="OBJECT"\r
- else:\r
- tname="unknown"\r
- print "%s.%sunknown integer type %d" % (self.name, name, size)\r
- if type & type_nullable:\r
- flags = ""\r
- else:\r
- flags = " NOT NULL"\r
- if type & type_localizable:\r
- flags += " LOCALIZABLE"\r
- fields[index] = "`%s` %s%s" % (name, tname, flags)\r
- if type & type_key:\r
- keys.append("`%s`" % name)\r
- fields = ", ".join(fields)\r
- keys = ", ".join(keys)\r
- return "CREATE TABLE %s (%s PRIMARY KEY %s)" % (self.name, fields, keys)\r
-\r
- def create(self, db):\r
- v = db.OpenView(self.sql())\r
- v.Execute(None)\r
- v.Close()\r
-\r
-class Binary:\r
- def __init__(self, fname):\r
- self.name = fname\r
- def __repr__(self):\r
- return 'msilib.Binary(os.path.join(dirname,"%s"))' % self.name\r
-\r
-def gen_schema(destpath, schemapath):\r
- d = MakeInstaller()\r
- schema = d.OpenDatabase(schemapath,\r
- win32com.client.constants.msiOpenDatabaseModeReadOnly)\r
-\r
- # XXX ORBER BY\r
- v=schema.OpenView("SELECT * FROM _Columns")\r
- curtable=None\r
- tables = []\r
- v.Execute(None)\r
- f = open(destpath, "wt")\r
- f.write("from msilib import Table\n")\r
- while 1:\r
- r=v.Fetch()\r
- if not r:break\r
- name=r.StringData(1)\r
- if curtable != name:\r
- f.write("\n%s = Table('%s')\n" % (name,name))\r
- curtable = name\r
- tables.append(name)\r
- f.write("%s.add_field(%d,'%s',%d)\n" %\r
- (name, r.IntegerData(2), r.StringData(3), r.IntegerData(4)))\r
- v.Close()\r
-\r
- f.write("\ntables=[%s]\n\n" % (", ".join(tables)))\r
-\r
- # Fill the _Validation table\r
- f.write("_Validation_records = [\n")\r
- v = schema.OpenView("SELECT * FROM _Validation")\r
- v.Execute(None)\r
- while 1:\r
- r = v.Fetch()\r
- if not r:break\r
- # Table, Column, Nullable\r
- f.write("(%s,%s,%s," %\r
- (`r.StringData(1)`, `r.StringData(2)`, `r.StringData(3)`))\r
- def put_int(i):\r
- if r.IsNull(i):f.write("None, ")\r
- else:f.write("%d," % r.IntegerData(i))\r
- def put_str(i):\r
- if r.IsNull(i):f.write("None, ")\r
- else:f.write("%s," % `r.StringData(i)`)\r
- put_int(4) # MinValue\r
- put_int(5) # MaxValue\r
- put_str(6) # KeyTable\r
- put_int(7) # KeyColumn\r
- put_str(8) # Category\r
- put_str(9) # Set\r
- put_str(10)# Description\r
- f.write("),\n")\r
- f.write("]\n\n")\r
-\r
- f.close()\r
-\r
-def gen_sequence(destpath, msipath):\r
- dir = os.path.dirname(destpath)\r
- d = MakeInstaller()\r
- seqmsi = d.OpenDatabase(msipath,\r
- win32com.client.constants.msiOpenDatabaseModeReadOnly)\r
-\r
- v = seqmsi.OpenView("SELECT * FROM _Tables");\r
- v.Execute(None)\r
- f = open(destpath, "w")\r
- print >>f, "import msilib,os;dirname=os.path.dirname(__file__)"\r
- tables = []\r
- while 1:\r
- r = v.Fetch()\r
- if not r:break\r
- table = r.StringData(1)\r
- tables.append(table)\r
- f.write("%s = [\n" % table)\r
- v1 = seqmsi.OpenView("SELECT * FROM `%s`" % table)\r
- v1.Execute(None)\r
- info = v1.ColumnInfo(constants.msiColumnInfoTypes)\r
- while 1:\r
- r = v1.Fetch()\r
- if not r:break\r
- rec = []\r
- for i in range(1,r.FieldCount+1):\r
- if r.IsNull(i):\r
- rec.append(None)\r
- elif info.StringData(i)[0] in "iI":\r
- rec.append(r.IntegerData(i))\r
- elif info.StringData(i)[0] in "slSL":\r
- rec.append(r.StringData(i))\r
- elif info.StringData(i)[0]=="v":\r
- size = r.DataSize(i)\r
- bytes = r.ReadStream(i, size, constants.msiReadStreamBytes)\r
- bytes = bytes.encode("latin-1") # binary data represented "as-is"\r
- if table == "Binary":\r
- fname = rec[0]+".bin"\r
- open(os.path.join(dir,fname),"wb").write(bytes)\r
- rec.append(Binary(fname))\r
- else:\r
- rec.append(bytes)\r
- else:\r
- raise "Unsupported column type", info.StringData(i)\r
- f.write(repr(tuple(rec))+",\n")\r
- v1.Close()\r
- f.write("]\n\n")\r
- v.Close()\r
- f.write("tables=%s\n" % repr(map(str,tables)))\r
- f.close()\r
-\r
-class _Unspecified:pass\r
-def change_sequence(seq, action, seqno=_Unspecified, cond = _Unspecified):\r
- "Change the sequence number of an action in a sequence list"\r
- for i in range(len(seq)):\r
- if seq[i][0] == action:\r
- if cond is _Unspecified:\r
- cond = seq[i][1]\r
- if seqno is _Unspecified:\r
- seqno = seq[i][2]\r
- seq[i] = (action, cond, seqno)\r
- return\r
- raise ValueError, "Action not found in sequence"\r
-\r
-def add_data(db, table, values):\r
- d = MakeInstaller()\r
- v = db.OpenView("SELECT * FROM `%s`" % table)\r
- count = v.ColumnInfo(0).FieldCount\r
- r = d.CreateRecord(count)\r
- for value in values:\r
- assert len(value) == count, value\r
- for i in range(count):\r
- field = value[i]\r
- if isinstance(field, (int, long)):\r
- r.SetIntegerData(i+1,field)\r
- elif isinstance(field, basestring):\r
- r.SetStringData(i+1,field)\r
- elif field is None:\r
- pass\r
- elif isinstance(field, Binary):\r
- r.SetStream(i+1, field.name)\r
- else:\r
- raise TypeError, "Unsupported type %s" % field.__class__.__name__\r
- v.Modify(win32com.client.constants.msiViewModifyInsert, r)\r
- r.ClearData()\r
- v.Close()\r
-\r
-def add_stream(db, name, path):\r
- d = MakeInstaller()\r
- v = db.OpenView("INSERT INTO _Streams (Name, Data) VALUES ('%s', ?)" % name)\r
- r = d.CreateRecord(1)\r
- r.SetStream(1, path)\r
- v.Execute(r)\r
- v.Close()\r
-\r
-def init_database(name, schema,\r
- ProductName, ProductCode, ProductVersion,\r
- Manufacturer,\r
- request_uac = False):\r
- try:\r
- os.unlink(name)\r
- except OSError:\r
- pass\r
- ProductCode = ProductCode.upper()\r
- d = MakeInstaller()\r
- # Create the database\r
- db = d.OpenDatabase(name,\r
- win32com.client.constants.msiOpenDatabaseModeCreate)\r
- # Create the tables\r
- for t in schema.tables:\r
- t.create(db)\r
- # Fill the validation table\r
- add_data(db, "_Validation", schema._Validation_records)\r
- # Initialize the summary information, allowing atmost 20 properties\r
- si = db.GetSummaryInformation(20)\r
- si.SetProperty(PID_TITLE, "Installation Database")\r
- si.SetProperty(PID_SUBJECT, ProductName)\r
- si.SetProperty(PID_AUTHOR, Manufacturer)\r
- si.SetProperty(PID_TEMPLATE, msi_type)\r
- si.SetProperty(PID_REVNUMBER, gen_uuid())\r
- if request_uac:\r
- wc = 2 # long file names, compressed, original media\r
- else:\r
- wc = 2 | 8 # +never invoke UAC\r
- si.SetProperty(PID_WORDCOUNT, wc)\r
- si.SetProperty(PID_PAGECOUNT, 200)\r
- si.SetProperty(PID_APPNAME, "Python MSI Library")\r
- # XXX more properties\r
- si.Persist()\r
- add_data(db, "Property", [\r
- ("ProductName", ProductName),\r
- ("ProductCode", ProductCode),\r
- ("ProductVersion", ProductVersion),\r
- ("Manufacturer", Manufacturer),\r
- ("ProductLanguage", "1033")])\r
- db.Commit()\r
- return db\r
-\r
-def add_tables(db, module):\r
- for table in module.tables:\r
- add_data(db, table, getattr(module, table))\r
-\r
-def make_id(str):\r
- #str = str.replace(".", "_") # colons are allowed\r
- str = str.replace(" ", "_")\r
- str = str.replace("-", "_")\r
- str = str.replace("+", "_")\r
- if str[0] in string.digits:\r
- str = "_"+str\r
- assert re.match("^[A-Za-z_][A-Za-z0-9_.]*$", str), "FILE"+str\r
- return str\r
-\r
-def gen_uuid():\r
- return str(pythoncom.CreateGuid())\r
-\r
-class CAB:\r
- def __init__(self, name):\r
- self.name = name\r
- self.file = open(name+".txt", "wt")\r
- self.filenames = sets.Set()\r
- self.index = 0\r
-\r
- def gen_id(self, dir, file):\r
- logical = _logical = make_id(file)\r
- pos = 1\r
- while logical in self.filenames:\r
- logical = "%s.%d" % (_logical, pos)\r
- pos += 1\r
- self.filenames.add(logical)\r
- return logical\r
-\r
- def append(self, full, file, logical = None):\r
- if os.path.isdir(full):\r
- return\r
- if not logical:\r
- logical = self.gen_id(dir, file)\r
- self.index += 1\r
- if full.find(" ")!=-1:\r
- print >>self.file, '"%s" %s' % (full, logical)\r
- else:\r
- print >>self.file, '%s %s' % (full, logical)\r
- return self.index, logical\r
-\r
- def commit(self, db):\r
- self.file.close()\r
- try:\r
- os.unlink(self.name+".cab")\r
- except OSError:\r
- pass\r
- for k, v in [(r"Software\Microsoft\VisualStudio\7.1\Setup\VS", "VS7CommonBinDir"),\r
- (r"Software\Microsoft\VisualStudio\8.0\Setup\VS", "VS7CommonBinDir"),\r
- (r"Software\Microsoft\VisualStudio\9.0\Setup\VS", "VS7CommonBinDir"),\r
- (r"Software\Microsoft\Win32SDK\Directories", "Install Dir"),\r
- ]:\r
- try:\r
- key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, k)\r
- dir = _winreg.QueryValueEx(key, v)[0]\r
- _winreg.CloseKey(key)\r
- except (WindowsError, IndexError):\r
- continue\r
- cabarc = os.path.join(dir, r"Bin", "cabarc.exe")\r
- if not os.path.exists(cabarc):\r
- continue\r
- break\r
- else:\r
- print "WARNING: cabarc.exe not found in registry"\r
- cabarc = "cabarc.exe"\r
- cmd = r'"%s" -m lzx:21 n %s.cab @%s.txt' % (cabarc, self.name, self.name)\r
- p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,\r
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\r
- for line in p.stdout:\r
- if line.startswith(" -- adding "):\r
- sys.stdout.write(".")\r
- else:\r
- sys.stdout.write(line)\r
- sys.stdout.flush()\r
- if not os.path.exists(self.name+".cab"):\r
- raise IOError, "cabarc failed"\r
- add_data(db, "Media",\r
- [(1, self.index, None, "#"+self.name, None, None)])\r
- add_stream(db, self.name, self.name+".cab")\r
- os.unlink(self.name+".txt")\r
- os.unlink(self.name+".cab")\r
- db.Commit()\r
-\r
-_directories = sets.Set()\r
-class Directory:\r
- def __init__(self, db, cab, basedir, physical, _logical, default, componentflags=None):\r
- """Create a new directory in the Directory table. There is a current component\r
- at each point in time for the directory, which is either explicitly created\r
- through start_component, or implicitly when files are added for the first\r
- time. Files are added into the current component, and into the cab file.\r
- To create a directory, a base directory object needs to be specified (can be\r
- None), the path to the physical directory, and a logical directory name.\r
- Default specifies the DefaultDir slot in the directory table. componentflags\r
- specifies the default flags that new components get."""\r
- index = 1\r
- _logical = make_id(_logical)\r
- logical = _logical\r
- while logical in _directories:\r
- logical = "%s%d" % (_logical, index)\r
- index += 1\r
- _directories.add(logical)\r
- self.db = db\r
- self.cab = cab\r
- self.basedir = basedir\r
- self.physical = physical\r
- self.logical = logical\r
- self.component = None\r
- self.short_names = sets.Set()\r
- self.ids = sets.Set()\r
- self.keyfiles = {}\r
- self.componentflags = componentflags\r
- if basedir:\r
- self.absolute = os.path.join(basedir.absolute, physical)\r
- blogical = basedir.logical\r
- else:\r
- self.absolute = physical\r
- blogical = None\r
- add_data(db, "Directory", [(logical, blogical, default)])\r
-\r
- def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None):\r
- """Add an entry to the Component table, and make this component the current for this\r
- directory. If no component name is given, the directory name is used. If no feature\r
- is given, the current feature is used. If no flags are given, the directory's default\r
- flags are used. If no keyfile is given, the KeyPath is left null in the Component\r
- table."""\r
- if flags is None:\r
- flags = self.componentflags\r
- if uuid is None:\r
- uuid = gen_uuid()\r
- else:\r
- uuid = uuid.upper()\r
- if component is None:\r
- component = self.logical\r
- self.component = component\r
- if Win64:\r
- flags |= 256\r
- if keyfile:\r
- keyid = self.cab.gen_id(self.absolute, keyfile)\r
- self.keyfiles[keyfile] = keyid\r
- else:\r
- keyid = None\r
- add_data(self.db, "Component",\r
- [(component, uuid, self.logical, flags, None, keyid)])\r
- if feature is None:\r
- feature = current_feature\r
- add_data(self.db, "FeatureComponents",\r
- [(feature.id, component)])\r
-\r
- def make_short(self, file):\r
- file = re.sub(r'[\?|><:/*"+,;=\[\]]', '_', file) # restrictions on short names\r
- parts = file.split(".")\r
- if len(parts)>1:\r
- suffix = parts[-1].upper()\r
- else:\r
- suffix = None\r
- prefix = parts[0].upper()\r
- if len(prefix) <= 8 and (not suffix or len(suffix)<=3):\r
- if suffix:\r
- file = prefix+"."+suffix\r
- else:\r
- file = prefix\r
- assert file not in self.short_names\r
- else:\r
- prefix = prefix[:6]\r
- if suffix:\r
- suffix = suffix[:3]\r
- pos = 1\r
- while 1:\r
- if suffix:\r
- file = "%s~%d.%s" % (prefix, pos, suffix)\r
- else:\r
- file = "%s~%d" % (prefix, pos)\r
- if file not in self.short_names: break\r
- pos += 1\r
- assert pos < 10000\r
- if pos in (10, 100, 1000):\r
- prefix = prefix[:-1]\r
- self.short_names.add(file)\r
- return file\r
-\r
- def add_file(self, file, src=None, version=None, language=None):\r
- """Add a file to the current component of the directory, starting a new one\r
- one if there is no current component. By default, the file name in the source\r
- and the file table will be identical. If the src file is specified, it is\r
- interpreted relative to the current directory. Optionally, a version and a\r
- language can be specified for the entry in the File table."""\r
- if not self.component:\r
- self.start_component(self.logical, current_feature)\r
- if not src:\r
- # Allow relative paths for file if src is not specified\r
- src = file\r
- file = os.path.basename(file)\r
- absolute = os.path.join(self.absolute, src)\r
- assert not re.search(r'[\?|><:/*]"', file) # restrictions on long names\r
- if self.keyfiles.has_key(file):\r
- logical = self.keyfiles[file]\r
- else:\r
- logical = None\r
- sequence, logical = self.cab.append(absolute, file, logical)\r
- assert logical not in self.ids\r
- self.ids.add(logical)\r
- short = self.make_short(file)\r
- full = "%s|%s" % (short, file)\r
- filesize = os.stat(absolute).st_size\r
- # constants.msidbFileAttributesVital\r
- # Compressed omitted, since it is the database default\r
- # could add r/o, system, hidden\r
- attributes = 512\r
- add_data(self.db, "File",\r
- [(logical, self.component, full, filesize, version,\r
- language, attributes, sequence)])\r
- if not version:\r
- # Add hash if the file is not versioned\r
- filehash = MakeInstaller().FileHash(absolute, 0)\r
- add_data(self.db, "MsiFileHash",\r
- [(logical, 0, filehash.IntegerData(1),\r
- filehash.IntegerData(2), filehash.IntegerData(3),\r
- filehash.IntegerData(4))])\r
- # Automatically remove .pyc/.pyo files on uninstall (2)\r
- # XXX: adding so many RemoveFile entries makes installer unbelievably\r
- # slow. So instead, we have to use wildcard remove entries\r
- # if file.endswith(".py"):\r
- # add_data(self.db, "RemoveFile",\r
- # [(logical+"c", self.component, "%sC|%sc" % (short, file),\r
- # self.logical, 2),\r
- # (logical+"o", self.component, "%sO|%so" % (short, file),\r
- # self.logical, 2)])\r
-\r
- def glob(self, pattern, exclude = None):\r
- """Add a list of files to the current component as specified in the\r
- glob pattern. Individual files can be excluded in the exclude list."""\r
- files = glob.glob1(self.absolute, pattern)\r
- for f in files:\r
- if exclude and f in exclude: continue\r
- self.add_file(f)\r
- return files\r
-\r
- def remove_pyc(self):\r
- "Remove .pyc/.pyo files on uninstall"\r
- add_data(self.db, "RemoveFile",\r
- [(self.component+"c", self.component, "*.pyc", self.logical, 2),\r
- (self.component+"o", self.component, "*.pyo", self.logical, 2)])\r
-\r
- def removefile(self, key, pattern):\r
- "Add a RemoveFile entry"\r
- add_data(self.db, "RemoveFile", [(self.component+key, self.component, pattern, self.logical, 2)])\r
-\r
-\r
-class Feature:\r
- def __init__(self, db, id, title, desc, display, level = 1,\r
- parent=None, directory = None, attributes=0):\r
- self.id = id\r
- if parent:\r
- parent = parent.id\r
- add_data(db, "Feature",\r
- [(id, parent, title, desc, display,\r
- level, directory, attributes)])\r
- def set_current(self):\r
- global current_feature\r
- current_feature = self\r
-\r
-class Control:\r
- def __init__(self, dlg, name):\r
- self.dlg = dlg\r
- self.name = name\r
-\r
- def event(self, ev, arg, cond = "1", order = None):\r
- add_data(self.dlg.db, "ControlEvent",\r
- [(self.dlg.name, self.name, ev, arg, cond, order)])\r
-\r
- def mapping(self, ev, attr):\r
- add_data(self.dlg.db, "EventMapping",\r
- [(self.dlg.name, self.name, ev, attr)])\r
-\r
- def condition(self, action, condition):\r
- add_data(self.dlg.db, "ControlCondition",\r
- [(self.dlg.name, self.name, action, condition)])\r
-\r
-class RadioButtonGroup(Control):\r
- def __init__(self, dlg, name, property):\r
- self.dlg = dlg\r
- self.name = name\r
- self.property = property\r
- self.index = 1\r
-\r
- def add(self, name, x, y, w, h, text, value = None):\r
- if value is None:\r
- value = name\r
- add_data(self.dlg.db, "RadioButton",\r
- [(self.property, self.index, value,\r
- x, y, w, h, text, None)])\r
- self.index += 1\r
-\r
-class Dialog:\r
- def __init__(self, db, name, x, y, w, h, attr, title, first, default, cancel):\r
- self.db = db\r
- self.name = name\r
- self.x, self.y, self.w, self.h = x,y,w,h\r
- add_data(db, "Dialog", [(name, x,y,w,h,attr,title,first,default,cancel)])\r
-\r
- def control(self, name, type, x, y, w, h, attr, prop, text, next, help):\r
- add_data(self.db, "Control",\r
- [(self.name, name, type, x, y, w, h, attr, prop, text, next, help)])\r
- return Control(self, name)\r
-\r
- def text(self, name, x, y, w, h, attr, text):\r
- return self.control(name, "Text", x, y, w, h, attr, None,\r
- text, None, None)\r
-\r
- def bitmap(self, name, x, y, w, h, text):\r
- return self.control(name, "Bitmap", x, y, w, h, 1, None, text, None, None)\r
-\r
- def line(self, name, x, y, w, h):\r
- return self.control(name, "Line", x, y, w, h, 1, None, None, None, None)\r
-\r
- def pushbutton(self, name, x, y, w, h, attr, text, next):\r
- return self.control(name, "PushButton", x, y, w, h, attr, None, text, next, None)\r
-\r
- def radiogroup(self, name, x, y, w, h, attr, prop, text, next):\r
- add_data(self.db, "Control",\r
- [(self.name, name, "RadioButtonGroup",\r
- x, y, w, h, attr, prop, text, next, None)])\r
- return RadioButtonGroup(self, name, prop)\r
-\r
- def checkbox(self, name, x, y, w, h, attr, prop, text, next):\r
- return self.control(name, "CheckBox", x, y, w, h, attr, prop, text, next, None)\r
-\r
-def pe_type(path):\r
- header = open(path, "rb").read(1000)\r
- # offset of PE header is at offset 0x3c\r
- pe_offset = struct.unpack("<i", header[0x3c:0x40])[0]\r
- assert header[pe_offset:pe_offset+4] == "PE\0\0"\r
- machine = struct.unpack("<H", header[pe_offset+4:pe_offset+6])[0]\r
- return machine\r
-\r
-def set_arch_from_file(path):\r
- global msi_type, Win64, arch_ext\r
- machine = pe_type(path)\r
- if machine == 0x14c:\r
- # i386\r
- msi_type = "Intel"\r
- Win64 = 0\r
- arch_ext = ''\r
- elif machine == 0x200:\r
- # Itanium\r
- msi_type = "Intel64"\r
- Win64 = 1\r
- arch_ext = '.ia64'\r
- elif machine == 0x8664:\r
- # AMD64\r
- msi_type = "x64"\r
- Win64 = 1\r
- arch_ext = '.amd64'\r
- else:\r
- raise ValueError, "Unsupported architecture"\r
- msi_type += ";1033"\r