Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3846 lines
177 KiB

  1. <job id='wi_compile'>
  2. <!-- includes for constants definitions -->
  3. <script language='VBScript' src='.\vbsconst.inc'></script>
  4. <script language='VBScript' src='.\xmlconst.inc'></script>
  5. <script language='VBScript' src='.\witables.inc'></script>
  6. <script language='VBScript' src='.\wival.inc'></script>
  7. <script language='VBScript' src='.\wiconst.inc'></script>
  8. <script language='VBScript' src='.\ritables.inc'></script>
  9. <!-- includes for function declarations -->
  10. <script language='VBScript' src='.\wixerror.inc'></script>
  11. <script language='VBScript' src='.\wixload.inc'></script>
  12. <!-- main -->
  13. <script Language='VBScript'>
  14. ' Compiles Windows Installer XML manifest into a Windows Installer database
  15. Option Explicit
  16. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  17. ' main
  18. Public xmldoc 'As XMLDOMDocument
  19. Public installer 'As Installer
  20. Public database 'As Database
  21. Public sumInfoArray(19)
  22. Public lastDiskId 'As integer
  23. Public featureDisplay 'As integer
  24. Public dictView 'As Dictionary
  25. Public dictVars 'As Dictionary
  26. Public dictStandardProperties 'As Dictionary
  27. Public dictFiles 'As Dictionary
  28. Public dictModules 'As Dictionary
  29. Public fso 'As FileSystemObject
  30. Public tempDir 'As String
  31. Public fCreate, fStyle, fTransform, fVerbose, fOpenModel, fHelp, fLogOps, fNoOnError, fNoModularize, fNoAddValidation
  32. Public fNoBinary, fNoUI, fNoSumInfo, fNoSeqTables, fNoLinkerInfo
  33. Public fModule ' if creating Merge Module
  34. Public fPatch ' if creating Patch
  35. Public moduleId 'As String
  36. Public fileSeq : fileSeq = 1 ' used to make file sequencing count each file
  37. Public regCount : regCount = 0 ' used to make registry keys unique
  38. Public osCount : osCount = 0 ' used for dummy primary key in redist os elements
  39. Public patchOrder : patchOrder = 1 ' used to make patch order count
  40. Public externalOrder : externalOrder = 1 ' used to make external file order count
  41. Public manifestPath, stylePath, databasePath, outputPath
  42. Public g_sBaseDir ' base directory used for update and CAB'ing
  43. Dim openMode, sumInfo, index
  44. ' Connect to Windows Installer, create dictionaries views, variables
  45. Set installer = Nothing
  46. Set installer = WScript.CreateObject("WindowsInstaller.Installer") : CheckError
  47. Set fso = WScript.CreateObject("Scripting.FileSystemObject") : CheckError
  48. Set dictView = WScript.CreateObject("Scripting.Dictionary") : CheckError
  49. Set dictVars = WScript.CreateObject("Scripting.Dictionary") : CheckError
  50. Set dictFiles = WScript.CreateObject("Scripting.Dictionary") : CheckError
  51. Set dictModules = WScript.CreateObject("Scripting.Dictionary") : CheckError
  52. ' properties standard in the Windows Installer that are not to be Modularized
  53. Set dictStandardProperties = WScript.CreateObject("Scripting.Dictionary") : CheckError
  54. dictStandardProperties.Add "TARGETDIR", ""
  55. dictStandardProperties.Add "Manufacturer", ""
  56. dictStandardProperties.Add "Privileged", ""
  57. dictStandardProperties.Add "ProductCode", ""
  58. dictStandardProperties.Add "ProductID", ""
  59. dictStandardProperties.Add "ProductLanguage", ""
  60. dictStandardProperties.Add "ProductName", ""
  61. dictStandardProperties.Add "ProductVersion", ""
  62. tempDir = installer.Environment("TMP")
  63. If Len(tempDir) = 0 Then tempDir = installer.Environment("TEMP")
  64. ParseCommandLine
  65. If Not fNoOnError Then On Error Resume Next
  66. If fHelp Or IsEmpty(manifestPath) Then
  67. ShowHelp
  68. WScript.Quit 1
  69. End If
  70. ' load manifest and apply any style sheets
  71. Dim rootElement : Set rootElement = LoadDocument(manifestPath, stylePath, dictVars)
  72. ' mark if this is a module or not
  73. If "Module" = rootElement.nodeName Then fModule = True Else fModule = False
  74. If "Patch" = rootElement.nodeName Then fPatch = True Else fPatch = False
  75. ' open or create new database according to defined schema
  76. If IsEmpty(outputPath) Then
  77. ' if a database wasn't specified use the base of the XML file and add the appropriate extension
  78. If IsEmpty(databasePath) Then
  79. Dim offset : offset = InStrRev(manifestPath, ".", -1, vbTextCompare)
  80. databasePath = Left(manifestPath, offset - 1)
  81. If fModule Then
  82. databasePath = databasePath & ".wixobj"
  83. ElseIf fPatch Then
  84. databasePath = databasePath & ".pcp"
  85. Else
  86. databasePath = databasePath & ".wixobj"
  87. End If
  88. fCreate = True
  89. End If
  90. If fTransform Then Fail "Must supply an output name for transform"
  91. If fCreate Then openMode = msiOpenDatabaseModeCreate Else openMode = msiOpenDatabaseModeTransact
  92. ElseIf fTransform Then
  93. openMode = "temptran.msi" ' temporary until we implement a better way
  94. Else
  95. openMode = outputPath
  96. End If
  97. Set database = installer.OpenDatabase(databasePath, openMode) : CheckError
  98. ' do the processing
  99. If fPatch Then
  100. ProcessPatchElement rootElement
  101. Else
  102. ProcessProductElement rootElement
  103. End If
  104. ' if this is a transform create the transform
  105. If fTransform Then
  106. Dim databaseRef : Set databaseRef = installer.OpenDatabase(databasePath, msiOpenDatabaseModeReadOnly) : CheckError
  107. database.GenerateTransform databaseRef, outputPath : CheckError
  108. database.CreateTransformSummaryInfo databaseRef, outputPath, 0, 0 : CheckError
  109. ' !! need to provide validation options in XML package element
  110. Set database = Nothing
  111. Else ' not creating a transform
  112. ' write the linker info
  113. If Not fNoLinkerInfo And Not fPatch Then WriteLinkerInfo
  114. If Not IsEmpty(sumInfoArray) Then
  115. If IsEmpty(outputPath) Then
  116. Set sumInfo = database.SummaryInformation(20) : CheckError
  117. Else
  118. database.Commit : CheckError
  119. Set database = Nothing
  120. Set sumInfo = installer.SummaryInformation(outputPath, 20) : CheckError
  121. End If
  122. ' write the summary information into the database
  123. For index = 1 To UBound(sumInfoArray)
  124. If Not IsEmpty(sumInfoArray(index)) Then sumInfo.Property(index) = sumInfoArray(index) : CheckError
  125. Next
  126. sumInfo.Persist : CheckError
  127. End If
  128. If IsEmpty(outputPath) Then
  129. database.Commit : CheckError
  130. Set database = Nothing
  131. End If
  132. End If
  133. Set dictVars = Nothing
  134. WScript.Quit 0
  135. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  136. ' Error handling and command-line parsing routines
  137. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' robmen ''
  138. ' ParseCommandLine
  139. Function ParseCommandLine()
  140. Dim arg, argIndex
  141. Dim chFlag
  142. If 0 = WScript.Arguments.Count Then fHelp = True : Exit Function
  143. For argIndex = 0 To WScript.Arguments.Count - 1
  144. arg = WScript.Arguments(argIndex)
  145. chFlag = AscW(arg)
  146. ' if this a variable
  147. If InStr(arg, "=") Then
  148. Dim expr : expr = Split(arg, "=")
  149. If IsNumeric(expr(1)) Then expr(1) = CLng(expr(1))
  150. dictVars.Item(expr(0)) = expr(1)
  151. ' command line parameter
  152. ElseIf (chFlag = AscW("/")) Or (chFlag = AscW("-")) Then
  153. chFlag = LCase(Mid(arg, 2))
  154. Select Case chFlag
  155. Case "a" ' stylesheet to apply
  156. argIndex = argIndex + 1
  157. stylePath = WScript.Arguments(argIndex)
  158. fStyle = True
  159. Case "b" ' base directory
  160. argIndex = argIndex + 1
  161. g_sBaseDir = WScript.Arguments(argIndex)
  162. Case "c" ' database to create
  163. argIndex = argIndex + 1
  164. databasePath = WScript.Arguments(argIndex)
  165. If fso.FileExists(databasePath) Then WScript.Echo "Warning, overwriting database: " & databasePath
  166. fCreate = True
  167. Case "d" ' database to update
  168. argIndex = argIndex + 1
  169. databasePath = WScript.Arguments(argIndex)
  170. If Not fso.FileExists(databasePath) Then Fail "Cannot find database: " & databasePath
  171. Case "t" ' transform
  172. argIndex = argIndex + 1
  173. outputPath = WScript.Arguments(argIndex)
  174. fTransform= True
  175. Case "sb" : fNoBinary = True
  176. Case "su" : fNoUI = True
  177. Case "ss" : fNoSumInfo = True
  178. Case "sq" : fNoSeqTables = True
  179. Case "sl" : fNoLinkerInfo = True
  180. Case "l" : fLogOps = True
  181. Case "o" : fOpenModel = True
  182. Case "v" : fVerbose = True
  183. Case "e" : fNoOnError = True
  184. Case "sm" : fNoModularize = True
  185. Case "sv" : fNoAddValidation = True
  186. Case "sf" : WScript.Echo "-sf has been deprecated"
  187. Case "sc" : WScript.Echo "-sc has been deprecated"
  188. Case "?" : fHelp = True
  189. Case Else : Fail "Invalid option flag: " & arg
  190. End Select
  191. ' must be the xml file
  192. Else
  193. If Not IsEmpty(manifestPath) Then Fail "Cannot specify two input xml documents"
  194. manifestPath = arg
  195. End If
  196. Next
  197. End Function ' ParseCommandLine
  198. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' robmen ''
  199. ' ShowHelp
  200. Sub ShowHelp()
  201. Dim sHelp
  202. sHelp = "candle - compiles Windows Installer Xml into a Windows Installer Database" & vbCrLf & _
  203. vbCrLf & _
  204. "candle.wsf [-?] [-sb] [-sl] [-sm] [-sq] [-ss] [-su] [-sv] [-i FilePaths]" & vbCrLf & _
  205. " [-t foo.mst] [-b basedir] [-a foo.wxs] [-c destfile.wixobj]" & vbCrLf & _
  206. " [-d foo.msi] [-l] [-o] [-v] [-e] foo.wxm" & vbCrLf & _
  207. vbCrLf & _
  208. " -a apply Windows installer Xml Stylesheet [default extension .wxs]" & vbCrLf & _
  209. " -b base directory for 'src' attributes" & vbCrLf & _
  210. " -c database / module to create from Windows installer Xml [will overwrite]" & vbCrLf & _
  211. " -d database to open and apply Windows installer Xml to [will not overwrite]" & vbCrLf & _
  212. " -e errors crash compiler, useful for debugging compiler" & vbCrLf & _
  213. " -i include paths to search (not yet implemented!)" & vbCrLf & _
  214. " -l log all operations, useful for debugging" & vbCrLf & _
  215. " -o open document model, ignores unexpected elements and attributes" & vbCrLf & _
  216. " -sb suppress processing of Binary-encoded data" & vbCrLf & _
  217. " -sc [DEPRECATED] suppress CAB'ing [valid only for Merge Modules]" & vbCrLf & _
  218. " CAB'ing done by linker [see light.wsf]" & vbCrLf & _
  219. " -sf [DEPRECATED] suppress updating the size, version, and language of files" & vbCrLf & _
  220. " updating file info done by linker [see light.wsf]" & vbCrLf & _
  221. " -sl suppress writing information for linker" & vbCrLf & _
  222. " -sm suppress modularization for merge modules" & vbCrLf & _
  223. " -sq suppress processing of Sequence elements" & vbCrLf & _
  224. " -ss suppress processing of Summary Information" & vbCrLf & _
  225. " -su suppress processing of UI elements" & vbCrLf & _
  226. " -sv suppress automatic creation of Validation table" & vbCrLf & _
  227. " -t transform to create [default extension .mst]" & vbCrLf & _
  228. " -v verbose output, useful for debugging" & vbCrLf & _
  229. " -? this help information" & vbCrLf & _
  230. vbCrLf & _
  231. "Common extensions:" & vbCrLf & _
  232. " .wxf - Windows installer Xml Fragment" & vbCrLf & _
  233. " .wxm - Windows installer Xml Module" & vbCrLf & _
  234. " .wxp - Windows installer Xml Product" & vbCrLf & _
  235. " .wxa - Windows installer Xml Patch" & vbCrLf & _
  236. " .wixobj - Windows installer Xml Object File (in MSI format)" & vbCrLf & _
  237. vbCrLf & _
  238. " .msm - Windows installer Merge Module" & vbCrLf & _
  239. " .msi - Windows installer Product Database" & vbCrLf & _
  240. " .mst - Windows installer Transform" & vbCrLf & _
  241. " .pcp - Windows installer Patch Creation Package" & vbCrLf & _
  242. vbCrLf & _
  243. "For more information see: http://compcat/wix"
  244. WScript.Echo sHelp
  245. End Sub ' ShowHelp
  246. Sub CheckError
  247. Dim message, errRec
  248. If Err = 0 Then Exit Sub
  249. message = Err.Source & " " & Hex(Err) & ": " & Err.Description
  250. If Not installer Is Nothing Then
  251. Set errRec = installer.LastErrorRecord
  252. If Not errRec Is Nothing Then message = message & vbNewLine & errRec.FormatText
  253. End If
  254. Fail message
  255. End Sub
  256. Sub Unexpected(child, parent)
  257. If Not fOpenModel Then
  258. Fail "Unexpected " & child.nodeTypeString & " node: " & child.nodeName & ", parent = " & parent.nodeName
  259. End If
  260. End Sub
  261. Function DosDate(convertdate)
  262. DosDate = 0 ' !!! TODO: do the conversion to Dos times
  263. End Function ' DosDate
  264. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  265. ' BaseDir
  266. Function BaseDir(sPath)
  267. If IsEmpty(g_sBaseDir) Then g_sBaseDir = "."
  268. If "sourcedir\" = LCase(Left(sPath, 10)) Then
  269. BaseDir = g_sBaseDir & Mid(sPath, 10)
  270. Else
  271. BaseDir = sPath
  272. End If
  273. End Function ' BaseDir
  274. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  275. ' Database processing routines
  276. Sub CreateTable(table)
  277. Dim primaryKeys, index, query, name
  278. For index = 1 To UBound(table)
  279. If Instr(table(index), " PRIMARY KEY") <> 0 Then
  280. If Not IsEmpty(primaryKeys) Then primaryKeys = primaryKeys & ","
  281. primaryKeys = primaryKeys & Split(table(index))(0)
  282. End If
  283. Next
  284. query = "CREATE TABLE " & Replace(Join(table, ","), ",", "(", 1, 1)
  285. query = Replace(query, " PRIMARY KEY", "")
  286. query = query & " PRIMARY KEY " & primaryKeys & ")"
  287. If fVerbose Then Wscript.Echo query
  288. database.OpenView(query).Execute : CheckError
  289. If Not fPatch And Not fNoAddValidation Then AddValidation installer, database, Replace(table(0), "`", ""), fVerbose
  290. ' some tables must exist to fulfill the Windows Installer's whims
  291. If table(0) = "`AppSearch`" And database.TablePersistent("Signature") = 2 Then CreateTable SignatureTable
  292. If table(0) = "`Dialog`" And database.TablePersistent("ListBox") = 2 Then CreateTable ListBoxTable
  293. If table(0) = "`Extension`" And database.TablePersistent("Verb") = 2 Then CreateTable VerbTable
  294. If table(0) = "`ProgId`" And database.TablePersistent("Extension") = 2 Then CreateTable ExtensionTable
  295. If table(0) = "`File`" Then CreateTable MsiFileHashTable
  296. End Sub
  297. Function CreateView(table)
  298. If Not fNoOnError Then On Error Resume Next
  299. If database.TablePersistent(Replace(table(0),"`","")) = 2 Then CreateTable(table)
  300. Set CreateView = database.OpenView("SELECT * FROM "& table(0)) : CheckError
  301. CreateView.Execute : CheckError
  302. End Function
  303. Sub CloseView(table)
  304. dictView.Remove(Replace(table(0),"`",""))
  305. End Sub
  306. Sub DoAction(table, op, row)
  307. If Not fNoOnError Then On Error Resume Next
  308. If IsEmpty(op) Then op = "merge" ' default supplied here rather than in the schema
  309. If fLogOps Then
  310. Dim format, index, name, delim
  311. delim = ": "
  312. For index = 1 To row.FieldCount
  313. format = format & "{" & delim & Split(table(index),"`")(1) & "=[" & index & "]}"
  314. delim = ", "
  315. Next
  316. row.StringData(0) = format
  317. Wscript.Echo Replace(table(0),"`"," ") & op & row.FormatText
  318. row.StringData(0) = ""
  319. End If
  320. ' Get existing view for table, else create new one and add to view dictionary
  321. Dim tableName : tableName = Replace(table(0),"`","")
  322. Dim view
  323. If dictView.Exists(tableName) Then
  324. Set view = dictView.Item(tableName)
  325. Else
  326. Set view = CreateView(table)
  327. dictView.Add tableName, view
  328. End If
  329. ' Select the update mode for processing the row with the view
  330. Dim modifyMode
  331. Select Case(op)
  332. Case "insert"
  333. modifyMode = msiViewModifyInsert
  334. Case "merge"
  335. modifyMode = msiViewModifyMerge
  336. Case "replace"
  337. modifyMode = msiViewModifyAssign
  338. Case "delete"
  339. view.Modify msiViewModifySeek, row
  340. If Err <> 0 Then Fail "'delete' item '" & row.StringData(1) & "' not present in table " & table(0)
  341. modifyMode = msiViewModifyDelete
  342. Case "exist"
  343. modifyMode = msiViewModifySeek
  344. Case "ensure"
  345. view.Modify msiViewModifySeek, row
  346. If Err <> 0 Then modifyMode = msiViewModifyInsert
  347. Case Else : Fail "Invalid op attribute value: " & op
  348. End Select
  349. view.Modify modifyMode, row
  350. If Err <> 0 Then Fail "Operation '" & op & "' failed for item '" & row.StringData(1) & "' in table " & table(0)
  351. End Sub
  352. ' append module.guid (note: guid is stored in moduleId) if this is a module and not a standard property
  353. Function ModularizeX(s)
  354. If fModule And Len(s) > 0 And Not dictStandardProperties.Exists(s) Then ModularizeX = s & "." & moduleId Else ModularizeX = s
  355. End Function
  356. Function Modularize(s)
  357. If fNoModularize Then Modularize = s Else Modularize = ModularizeX(s)
  358. End Function
  359. ' append module.guid if this is a non-standard property and it is a module
  360. Function ModularizeProperty(s)
  361. Dim nStart, nPropStart, nPropEnd, sProp
  362. If fModule And Not fNoModularize And Len(s) > 0 Then
  363. nStart = 1
  364. Do
  365. nPropStart = InStr(nStart, s, "[")
  366. nPropEnd = InStr(nStart, s, "]")
  367. If nPropEnd > nPropStart Then
  368. sProp = Mid(s, nPropStart + 1, nPropEnd - nPropStart - 1)
  369. If Not dictStandardProperties.Exists(sProp) Then
  370. s = Left(s, nPropEnd - 1) & "." & moduleId & Mid(s, nPropEnd)
  371. nPropEnd = nPropEnd + 37 ' slide past the .GUID
  372. End If
  373. Else
  374. Exit Do
  375. End If
  376. nStart = nPropEnd + 1' move past the close bracket
  377. Loop
  378. End If
  379. ModularizeProperty = s
  380. End Function ' ModularizeProperty
  381. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  382. ' Linker information routines
  383. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  384. ' WriteLinkerInfo
  385. Sub WriteLinkerInfo
  386. Dim vw, vw2, rec, rec2
  387. Dim op
  388. If 2 = database.TablePersistent("candle_Info") Then
  389. Set vw = database.OpenView("CREATE TABLE `candle_Info` (`LinkProperty` CHAR(0) NOT NULL, `Value` CHAR(0) PRIMARY KEY `LinkProperty`)")
  390. vw.Execute
  391. End If
  392. Set vw = database.OpenView("SELECT `Value` FROM `candle_Info` WHERE `LinkProperty`=?")
  393. Set vw2 = database.OpenView("SELECT `LinkProperty`, `Value` FROM `candle_Info`")
  394. Set rec = installer.CreateRecord(1)
  395. rec.StringData(1) = "SourceFile"
  396. vw.Execute rec
  397. Set rec2 = vw.Fetch
  398. If rec2 Is Nothing Then
  399. Set rec2 = installer.CreateRecord(2)
  400. rec2.StringData(1) = "SourceFile"
  401. rec2.StringData(2) = manifestPath
  402. vw2.Modify msiViewModifyInsert, rec2
  403. Else
  404. rec2.StringData(1) = manifestPath
  405. vw.Modify msiViewModifyUpdate, rec2
  406. End If
  407. rec.StringData(1) = "IsModule"
  408. vw.Execute rec
  409. Set rec2 = vw.Fetch
  410. If rec2 Is Nothing Then
  411. Set rec2 = installer.CreateRecord(2)
  412. rec2.StringData(1) = "IsModule"
  413. If fModule Then rec2.StringData(2) = "1" Else rec2.StringData(2) = "0"
  414. vw2.Modify msiViewModifyInsert, rec2
  415. Else
  416. If fModule Then rec2.StringData(1) = "1" Else rec2.StringData(1) = "0"
  417. vw.Modify msiViewModifyUpdate, rec2
  418. End If
  419. WriteFileInfo
  420. If Not fModule Then WriteModuleInfo
  421. End Sub ' WriteLinkerInfo
  422. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  423. ' WriteFileInfo
  424. Sub WriteFileInfo
  425. Dim vwDisk, vwFile, rec
  426. Dim sKey, aData
  427. If 0 = dictFiles.Count Then Exit Sub
  428. If 2 = database.TablePersistent("candle_Files") Then
  429. Set vwFile = database.OpenView("CREATE TABLE `candle_Files` (`File_` CHAR(72) NOT NULL, `Path` CHAR(0) NOT NULL PRIMARY KEY `File_`)")
  430. vwFile.Execute
  431. End If
  432. Set vwFile = database.OpenView("SELECT `File_`, `Path` FROM `candle_Files`")
  433. vwFile.Execute
  434. If 2 = database.TablePersistent("candle_DiskInfo") Then
  435. Set vwDisk = database.OpenView("CREATE TABLE `candle_DiskInfo` (`Identifier` CHAR(72) NOT NULL, `DiskId` INTEGER NOT NULL, `IsModule` INTEGER PRIMARY KEY `Identifier`)")
  436. vwDisk.Execute
  437. End If
  438. Set vwDisk = database.OpenView("SELECT `Identifier`, `DiskId`, `IsModule` FROM `candle_DiskInfo`")
  439. vwDisk.Execute
  440. Set rec = installer.CreateRecord(3)
  441. For Each sKey In dictFiles
  442. aData = dictFiles.Item(sKey)
  443. rec.StringData(1) = sKey
  444. rec.StringData(2) = aData(0)
  445. vwFile.Modify msiViewModifyInsert, rec
  446. rec.StringData(1) = sKey
  447. rec.IntegerData(2) = CInt(aData(1))
  448. rec.IntegerData(3) = 0
  449. vwDisk.Modify msiViewModifyInsert, rec
  450. Next
  451. End Sub ' WriteFileInfo
  452. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  453. ' WriteModuleInfo
  454. Sub WriteModuleInfo
  455. Dim vwDisk, vwModule, rec
  456. Dim sKey, aData
  457. Dim sPrimaryFeature, aSecondaryFeatures, i, sConnectFeatures
  458. If 0 = dictModules.Count Then Exit Sub
  459. If 2 = database.TablePersistent("candle_Modules") Then
  460. Set vwModule = database.OpenView("CREATE TABLE `candle_Modules` (`Module` CHAR(72) NOT NULL, `Path` CHAR(0) NOT NULL, `Language` INTEGER NOT NULL, `PrimaryFeature_` CHAR(38) NOT NULL, `ConnectFeatures_` CHAR(0), `RedirectDirectory_` CHAR(72) PRIMARY KEY `Module`)")
  461. vwModule.Execute
  462. End If
  463. Set vwModule = database.OpenView("SELECT `Module`, `Path`, `Language`, `PrimaryFeature_`, `ConnectFeatures_`, `RedirectDirectory_` FROM `candle_Modules`")
  464. vwModule.Execute
  465. If 2 = database.TablePersistent("candle_DiskInfo") Then
  466. Set vwDisk = database.OpenView("CREATE TABLE `candle_DiskInfo` (`Identifier` CHAR(72) NOT NULL, `DiskId` INTEGER NOT NULL, `IsModule` INTEGER PRIMARY KEY `Identifier`)")
  467. vwDisk.Execute
  468. End If
  469. Set vwDisk = database.OpenView("SELECT `Identifier`, `DiskId`, `IsModule` FROM `candle_DiskInfo`")
  470. vwDisk.Execute
  471. Set rec = installer.CreateRecord(6)
  472. For Each sKey In dictModules
  473. aData = dictModules.Item(sKey)
  474. If IsEmpty(aData(4)) Then
  475. aSecondaryFeatures = Split(aData(5), ":")
  476. If -1 = UBound(aSecondaryFeatures) Then Fail "Error, Module: " & sKey & " not part of any Features"
  477. If 0 < UBound(aSecondaryFeatures) Then Fail "Error, Module: " & sKey & " is part of many Features, but no Feature is marked primary"
  478. sPrimaryFeature = aSecondaryFeatures(0)
  479. For i = 1 To UBound(aSecondaryFeatures)
  480. If 1 < i Then sConnectFeatures = sConnectFeatures & ":"
  481. sConnectFeatures = sConnectFeatures & aSecondaryFeatures(i)
  482. Next
  483. Else
  484. sPrimaryFeature = aData(4)
  485. sConnectFeatures = aData(5)
  486. End If
  487. rec.StringData(1) = sKey
  488. rec.StringData(2) = aData(0)
  489. rec.IntegerData(3) = CInt(aData(2))
  490. rec.StringData(4) = sPrimaryFeature
  491. rec.StringData(5) = sConnectFeatures
  492. rec.StringData(6) = aData(3)
  493. 'WScript.Echo "X Directory: " & aData(3)
  494. vwModule.Modify msiViewModifyInsert, rec
  495. rec.StringData(1) = sKey
  496. rec.IntegerData(2) = CInt(aData(1))
  497. rec.IntegerData(3) = 1
  498. vwDisk.Modify msiViewModifyInsert, rec
  499. Next
  500. End Sub ' WriteModuleInfo
  501. '---------------------------------------------------------------------------------'
  502. ' XML parsing routines and conditional execution logic
  503. '---------------------------------------------------------------------------------'
  504. Function ElementHasText(node)
  505. ElementHasText = Not node.selectSingleNode("text()") Is Nothing
  506. End Function
  507. Function ElementText(node)
  508. If node Is Nothing Then Fail "passed dead node to ElementText"
  509. Dim child : Set child = node.selectSingleNode("text()")
  510. If child Is Nothing Then Fail "Missing text value for element: " & node.nodeName
  511. ElementText = child.text
  512. End Function
  513. Function LoadDocument(path, stylePath, dictVars)
  514. Dim xmlDoc : Set xmlDoc = WixLoad(path, Empty, dictVars, True)
  515. If Not IsEmpty(stylePath) Then
  516. WixApplyStyleSheet xmlDoc, stylePath
  517. If fVerbose Then
  518. WScript.Echo "--------------------"
  519. WScript.Echo "Transformed manifest:"
  520. WScript.Echo xmlDoc.xml
  521. WScript.Echo
  522. End If
  523. End If
  524. ' return the root of the document
  525. Set LoadDocument = xmlDoc.documentElement
  526. End Function
  527. Function GetEncoding(node)
  528. Dim xmldecl : Set xmldecl = node.ownerDocument.selectSingleNode("pi('xml')")
  529. If (Not xmldecl Is Nothing) Then
  530. Dim encattr 'As IXMLDOMNode
  531. Set encattr = xmldecl.Attributes.getNamedItem("encoding")
  532. If Not encattr Is Nothing Then GetEncoding = encattr.Text
  533. End If
  534. End Function
  535. Function NameToBit(names, name, value)
  536. Dim index, bit
  537. bit = 1
  538. For index = 0 To UBound(names)
  539. If names(index) = name Then
  540. If value = "yes" Then NameToBit = bit Else NameToBit = 0
  541. Exit Function
  542. End If
  543. If bit = &h40000000 Then bit = &h80000000 Else bit = bit + bit
  544. Next
  545. End Function
  546. Function GetElementName(node)
  547. GetElementName = Empty
  548. If node.nodeType = NODE_ELEMENT Then GetElementName = node.nodeName
  549. End Function
  550. '---------------------------------------------------------------------------------'
  551. ' Non-UI element handlers
  552. '---------------------------------------------------------------------------------'
  553. Public productCode, productName, productLanguage, productAuthor ' product properties used as defaults for suminfo
  554. Sub ProcessProductElement(node)
  555. If Not fNoOnError Then On Error Resume Next
  556. Dim child, attribute, value, op, version, sumInfo, index
  557. ' Walk XML nodes and populate tables
  558. lastDiskId = 0
  559. featureDisplay = 0
  560. For Each attribute In node.Attributes
  561. value = attribute.value
  562. Select Case(attribute.name)
  563. Case "op" : op = value
  564. Case "Name" : productName = value : If Not fModule Then ProcessProperty "ProductName", value, "replace"
  565. Case "Id" : productCode = "{"&value&"}" : If Not fModule And Not IsEmpty(value) And value <> "" Then ProcessProperty "ProductCode", productCode, "replace" Else moduleId = Replace(value, "-", "_")
  566. Case "UpgradeCode" : value = "{"&value&"}" : If Not fModule Then ProcessProperty "UpgradeCode", value, "replace"
  567. Case "Manufacturer": productAuthor = value : If Not fModule Then ProcessProperty "Manufacturer", value, "replace"
  568. Case "Language" : productLanguage = value : If Not fModule Then ProcessProperty "ProductLanguage", value, "replace"
  569. Case "Version" : version = value : If Not fModule Then ProcessProperty "ProductVersion", value, "replace"
  570. Case "xmlns" : ' ProcessProperty "XMLSchema", value, "replace"
  571. Case Else : Unexpected attribute, node
  572. End Select
  573. Next
  574. If openMode = fCreate And IsEmpty(productCode) Then Fail "Id attribute required for created database"
  575. If fModule Then
  576. Dim row : Set row = installer.CreateRecord(UBound(ModuleSignatureTable))
  577. row.StringData (ModuleSignature_ModuleID) = ModularizeX(productName)
  578. row.StringData (ModuleSignature_Language) = productLanguage
  579. row.StringData (ModuleSignature_Version) = version
  580. DoAction ModuleSignatureTable, op, row
  581. ' if there is no FeatureComponents table add it
  582. If database.TablePersistent(Replace(FeatureComponentsTable(0),"`","")) = 2 Then CreateTable(FeatureComponentsTable)
  583. End If
  584. ProcessProductChildElements(node)
  585. Set dictView = Nothing ' close all views, could also use RemoveAll method of Dictionary object
  586. End Sub ' ProcessProductElement
  587. Sub ProcessSummaryInformation(node)
  588. If Not fNoOnError Then On Error Resume Next
  589. Dim sumInfo
  590. Dim attribute, op, value, sourceBits
  591. Dim packageCode, packageLanguage, packageAuthor, packageName, msiVersion, keywords, comments, codepage, platform
  592. If fCreate Then ' default unspecified package properties to product properties if creating a new package
  593. sumInfoArray(2) = "Installation Database"
  594. sumInfoArray(12) = Now
  595. sumInfoArray(18) = "Windows Installer XML (candle/light)"
  596. sumInfoArray(19) = 1 'Read-only recommended
  597. packageCode = productCode
  598. packageName = productName
  599. packageAuthor = productAuthor
  600. packageLanguage = productLanguage
  601. msiVersion = 100 ' lowest released version, really should be specified
  602. codepage = 0 ' neutral, really should be specified
  603. keywords = "Installer"
  604. sourceBits = 0
  605. Else
  606. Set sumInfo = database.SummaryInformation(0) : CheckError
  607. sumInfoArray(2) = sumInfo.Property(2)
  608. sumInfoArray(12) = sumInfo.Property(12)
  609. sumInfoArray(18) = sumInfo.Property(18)
  610. sumInfoArray(19) = sumInfo.Property(19)
  611. codepage = sumInfo.Property(1)
  612. packageName = sumInfo.Property(3)
  613. packageAuthor = sumInfo.Property(4)
  614. packageCode = sumInfo.Property(9)
  615. msiVersion = sumInfo.Property(14)
  616. sourceBits = sumInfo.Property(15)
  617. value = Split(sumInfo.Property(7), ";")
  618. platform = value(0)
  619. If UBound(value) = 1 Then packageLanguage = value(1)
  620. End If
  621. For Each attribute In node.Attributes
  622. value = attribute.value
  623. Select Case(attribute.name)
  624. Case "op" : op = value
  625. Case "Value" : value = value
  626. Case "Id" : packageCode = "{"&value&"}"
  627. Case "Description" : packageName = value
  628. Case "Manufacturer" : packageAuthor = value
  629. Case "Languages" : packageLanguage = value
  630. Case "Platforms" : platform = value
  631. Case "InstallerVersion": msiVersion = CInt(value)
  632. Case "Keywords" : keywords = value
  633. Case "Comments" : comments = value
  634. Case "SummaryCodepage" : codepage = CInt(value)
  635. Case "ShortNames": If value="yes" Then sourceBits=sourceBits Or 1 Else If value="no" Then sourceBits=sourceBits And Not 1
  636. Case "Compressed": If value="yes" Then sourceBits=sourceBits Or 2 Else If value="no" Then sourceBits=sourceBits And Not 2
  637. Case "AdminImage": If value="yes" Then sourceBits=sourceBits Or 4 Else If value="no" Then sourceBits=sourceBits And Not 4
  638. Case Else : Unexpected attribute, node
  639. End Select
  640. Next
  641. sumInfoArray(1) = codepage
  642. sumInfoArray(3) = packageName
  643. sumInfoArray(4) = packageAuthor
  644. sumInfoArray(5) = keywords
  645. sumInfoArray(6) = comments
  646. sumInfoArray(7) = platform & ";" & packageLanguage
  647. sumInfoArray(9) = packageCode
  648. sumInfoArray(13)= Now
  649. sumInfoArray(14)= msiVersion
  650. sumInfoArray(15)= sourceBits
  651. End Sub
  652. Sub ProcessProductChildElements(node)
  653. Dim child
  654. For Each child In node.childNodes
  655. Select Case (GetElementName(child))
  656. Case Empty
  657. Case "Redist" : ProcessRedistElement child
  658. Case "Condition" : ProcessLaunchCondition child
  659. Case "Property" : ProcessPropertyElement child
  660. Case "Directory" : ProcessDirectoryElement child, Empty, Empty
  661. Case "Component" : ProcessComponentElement child, Empty, Empty ' !!! passing Empty for sPath is not the right thing to do
  662. Case "Feature" : ProcessFeatureElement child, Empty, featureDisplay
  663. Case "Media" : ProcessMediaElement child, lastDiskId
  664. Case "AppId" : ProcessAppIdElement child
  665. Case "CustomAction" : ProcessCustomActionElement child
  666. Case "CustomTable" : ProcessCustomTableElement child
  667. Case "UI": If Not fNoUI Then ProcessUIElement child
  668. Case "Package": If Not fNoSumInfo Then ProcessSummaryInformation child
  669. Case "InstallExecuteSequence" : ProcessSequence InstallExecuteSequenceTable, child
  670. Case "InstallUISequence" : ProcessSequence InstallUISequenceTable, child
  671. Case "AdminExecuteSequence" : ProcessSequence AdminExecuteSequenceTable, child
  672. Case "AdminUISequence" : ProcessSequence AdminUISequenceTable, child
  673. Case "AdvtExecuteSequence" : ProcessSequence AdvtExecuteSequenceTable, child
  674. Case "AdvertiseExecuteSequence" : ProcessSequence AdvtExecuteSequenceTable, child
  675. Case "AdvtUISequence" : ProcessSequence AdvtUISequenceTable, child
  676. Case "AdvertiseUISequence" : ProcessSequence AdvtUISequenceTable, child
  677. Case "Binary" : ProcessBinaryElement child, BinaryTable
  678. Case "Icon" : ProcessBinaryElement child, IconTable
  679. Case "Dependency" : ProcessDependencyElement child
  680. Case Else : Unexpected child, node
  681. End Select
  682. Next
  683. End Sub
  684. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  685. ' redist elements
  686. Sub ProcessRedistElement(node)
  687. Dim attribute, child, row, op, sDescription, sDistribution, sType
  688. Set row = installer.CreateRecord(UBound(RedistInfoTable))
  689. For Each attribute In node.Attributes
  690. Select Case(attribute.name)
  691. Case "op" : op = attribute.value
  692. Case "Distribution" : sDistribution = attribute.value
  693. Case "Type" : sType = attribute.value
  694. Case Else : Unexpected attribute, node
  695. End Select
  696. Next
  697. If IsEmpty(sDistribution) Or sDistribution = "internal" Then
  698. row.IntegerData(RedistInfo_Distribution) = 0
  699. ElseIf sDistribution = "external" Then
  700. row.IntegerData(RedistInfo_Distribution) = 1
  701. Else
  702. Fail "Unexpected Redist.Distribution: " & sDistribution
  703. End If
  704. If IsEmpty(sType) Or sType = "retail" Then
  705. row.IntegerData(RedistInfo_Type) = 1
  706. ElseIf sType = "debug" Then
  707. row.IntegerData(RedistInfo_Type) = 0
  708. Else
  709. Fail "Unexpected Redist.Type: " & sType
  710. End If
  711. For Each child In node.childNodes
  712. Select Case (GetElementName(child))
  713. Case Empty
  714. Case "Description" : If IsEmpty(sDescription) Then sDescription = ElementText(child) Else Fail "Cannot have two Redist.Description elements"
  715. ' Case "Keyword" : ProcessRedistKeywordElement child
  716. Case "Contact" : ProcessRedistContactElement child
  717. Case "AllowUser" : ProcessRedistAllowUserElement child
  718. Case "Windows9x" : ProcessOsElement child
  719. Case "Windows32" : ProcessOsElement child
  720. Case "Windows64" : ProcessOsElement child
  721. Case Else : Unexpected child, node
  722. End Select
  723. Next
  724. row.StringData(RedistInfo_DummyPk) = "RedistPack"
  725. row.StringData(RedistInfo_Description) = sDescription
  726. DoAction RedistInfoTable, op, row
  727. End Sub
  728. ' Sub ProcessRedistKeywordElement(node)
  729. ' Dim attribute, row, op
  730. ' Set row = installer.CreateRecord(UBound(RedistKeywordsTable))
  731. ' For Each attribute In node.Attributes
  732. ' Select Case(attribute.name)
  733. ' Case "op" : op = attribute.value
  734. ' Case Else : Unexpected attribute, node
  735. ' End Select
  736. ' Next
  737. ' row.StringData(RedistKeywords_Keyword) = ElementText(node)
  738. ' DoAction RedistKeywordsTable, op, row
  739. ' End Sub
  740. Sub ProcessRedistContactElement(node)
  741. Dim attribute, row, op
  742. Set row = installer.CreateRecord(UBound(RedistContactsTable))
  743. For Each attribute In node.Attributes
  744. Select Case(attribute.name)
  745. Case "op" : op = attribute.value
  746. Case Else : Unexpected attribute, node
  747. End Select
  748. Next
  749. row.StringData(RedistContacts_Contact) = ElementText(node)
  750. DoAction RedistContactsTable, op, row
  751. End Sub
  752. Sub ProcessRedistAllowUserElement(node)
  753. Dim attribute, row, op, sDomain, sAlias
  754. Set row = installer.CreateRecord(UBound(RedistPermissionsTable))
  755. For Each attribute In node.Attributes
  756. Select Case(attribute.name)
  757. Case "op" : op = attribute.value
  758. Case "Domain" : sDomain = attribute.value
  759. Case "Alias" : sAlias = attribute.value
  760. Case Else : Unexpected attribute, node
  761. End Select
  762. Next
  763. If IsEmpty(sDomain) Then sDomain = "REDMOND"
  764. If IsEmpty(sAlias) Then Fail "Must specify an alias for Permission elements"
  765. row.StringData(RedistPermissions_Domain) = sDomain
  766. row.StringData(RedistPermissions_Alias) = sAlias
  767. DoAction RedistPermissionsTable, op, row
  768. End Sub
  769. Sub ProcessOsElement(node)
  770. Dim attribute, row, op
  771. Set row = installer.CreateRecord(UBound(RedistOsTable))
  772. row.IntegerData(RedistOs_DummyPk) = osCount
  773. row.StringData(RedistOs_Type) = GetElementName(node)
  774. For Each attribute In node.Attributes
  775. Select Case(attribute.name)
  776. Case "op" : op = attribute.value
  777. Case "Flavor" : row.StringData(RedistOs_Flavor) = attribute.value
  778. Case "Language" : row.StringData(RedistOs_Language) = attribute.value
  779. Case "MinVersion" : row.StringData(RedistOs_MinVersion) = attribute.value
  780. Case "MaxVersion" : row.StringData(RedistOs_MaxVersion) = attribute.value
  781. Case Else : Unexpected attribute, node
  782. End Select
  783. Next
  784. osCount = osCount + 1
  785. DoAction RedistOsTable, op, row
  786. End Sub
  787. ' end redist elements
  788. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  789. Sub ProcessProperty(property, value, op)
  790. Dim row : Set row = installer.CreateRecord(UBound(PropertyTable))
  791. row.StringData (Property_Property) = Modularize(property)
  792. row.StringData (Property_Value) = value
  793. DoAction PropertyTable, op, row
  794. End Sub
  795. Sub ProcessPropertyElement(node)
  796. Dim attribute, op, child, control, table, property, order, value, fAppSearch, signature
  797. For Each attribute In node.Attributes
  798. Select Case(attribute.name)
  799. Case "op" : op = attribute.value
  800. Case "Value" : value = attribute.value
  801. Case Else : Unexpected attribute, node
  802. End Select
  803. Next
  804. property = ElementText(node)
  805. ' see if this property is used for AppSearch
  806. fAppSearch = False
  807. For Each child In node.childNodes
  808. Select Case (GetElementName(child))
  809. Case Empty
  810. Case "IniFileSearch" : If fAppSearch Then Fail "Only one search type can appear under Property: " & property Else fAppSearch = True : signature = ProcessIniFileSearchElement (child)
  811. Case "RegistrySearch" : If fAppSearch Then Fail "Only one search type can appear under Property: " & property Else fAppSearch = True : signature = ProcessRegistrySearchElement (child)
  812. Case "ComponentSearch": If fAppSearch Then Fail "Only one search type can appear under Property: " & property Else fAppSearch = True : signature = ProcessComponentSearchElement(child)
  813. Case "DirectorySearch": If fAppSearch Then Fail "Only one search type can appear under Property: " & property Else fAppSearch = True : signature = ProcessDirectorySearchElement(child, "")
  814. Case "FileSearch" : If fAppSearch Then Fail "Only one search type can appear under Property: " & property Else fAppSearch = True : signature = ProcessFileSearchElement (child, "")
  815. Case Else : Unexpected child, node
  816. End Select
  817. Next
  818. If fAppSearch Then
  819. If Not IsEmpty(value) Then Fail "Cannot specify a Value for search Property: " & property
  820. ProcessAppSearch property, signature, op
  821. Else
  822. ProcessProperty property, value, op
  823. End If
  824. End Sub
  825. Sub ProcessLaunchCondition(node)
  826. Dim attribute, row, op
  827. Set row = installer.CreateRecord(UBound(LaunchConditionTable))
  828. For Each attribute In node.Attributes
  829. Select Case(attribute.name)
  830. Case "op" : op = attribute.value
  831. Case "Message" : row.StringData(LaunchCondition_Description) = attribute.value
  832. Case Else : Unexpected attribute, node
  833. End Select
  834. Next
  835. row.StringData(LaunchCondition_Condition) = ElementText(node)
  836. DoAction LaunchConditionTable, op, row
  837. End Sub
  838. Sub ProcessDirectoryElement(node, parent, sPath)
  839. Dim child, directory, value, attribute, row, op, name, longName, sTarget, sourceName, longSource, sSource, sDefaultDir
  840. Dim bStdDir, bStdParent
  841. bStdDir = False
  842. bStdParent = False
  843. directory = ElementText(node)
  844. Set row = installer.CreateRecord(UBound(DirectoryTable))
  845. ' don't modularize TARGETDIR
  846. If "TARGETDIR" = directory Or bStdDir Then row.StringData(Directory_Directory) = directory Else row.StringData(Directory_Directory) = Modularize(directory)
  847. If "TARGETDIR" = parent Or bStdParent Then row.StringData(Directory_Directory_Parent) = parent Else row.StringData(Directory_Directory_Parent) = Modularize(parent)
  848. For Each attribute In node.Attributes
  849. value = attribute.value
  850. Select Case(attribute.name)
  851. Case "op" : op = value
  852. Case "Name" : name = value
  853. Case "LongName" : longName = value
  854. Case "SourceName" : sourceName = value
  855. Case "LongSource" : longSource = value
  856. Case Else : Unexpected attribute, node
  857. End Select
  858. Next
  859. If Len(name) = 0 Then Fail "Must specify a Name for Directory: " & directory
  860. If IsEmpty(sourceName) And Not IsEmpty(longSource) Then Fail "Must specify a SourceName for Directory: " & directory
  861. sTarget = name
  862. If Not IsEmpty(longName) Then sTarget = sTarget & "|" & longName
  863. sSource = sourceName
  864. If Not IsEmpty(longSource) Then sSource = sSource & "|" & longSource
  865. sDefaultDir = sTarget
  866. If Not IsEmpty(sSource) Then sDefaultDir = sDefaultDir & ":" & sSource
  867. row.StringData(Directory_DefaultDir) = sDefaultDir
  868. REM DefaultDir required if "insert" or "replace" or "merge"
  869. REM If IsEmpty(parent) And op <> "exist" Then Fail "Root Directory element op must be 'exist'"
  870. DoAction DirectoryTable, op, row
  871. ' build up the path
  872. If Not IsEmpty(sourceName) Then sPath = sPath & sourceName & "\" Else sPath = sPath & name & "\"
  873. If fModule Then
  874. If dictStdDirs.Exists(directory) Then bStdDir = True Else bStdDir = False
  875. ' if adding one of the standard Windows Installer directories to module
  876. If bStdDir Then
  877. If database.TablePersistent("CustomAction") = 2 Then CreateTable CustomActionTable
  878. If database.TablePersistent("InstallExecuteSequence") = 2 Then CreateTable InstallExecuteSequenceTable
  879. If database.TablePersistent("InstallUISequence") = 2 Then CreateTable InstallUISequenceTable
  880. If database.TablePersistent("AdminExecuteSequence") = 2 Then CreateTable AdminExecuteSequenceTable
  881. If database.TablePersistent("AdminUISequence") = 2 Then CreateTable AdminUISequenceTable
  882. If database.TablePersistent("AdvtExecuteSequence") = 2 Then CreateTable AdvtExecuteSequenceTable
  883. If database.TablePersistent("AdvtUISequence") = 2 Then CreateTable AdvtUISequenceTable
  884. End If
  885. End If
  886. For Each child In node.childNodes
  887. Select Case (GetElementName(child))
  888. Case Empty
  889. Case "Directory" : ProcessDirectoryElement child, (directory), (sPath)
  890. Case "Module" : ProcessDirectoryModuleElement child, (directory)
  891. Case "Component" : ProcessComponentElement child, (directory), (sPath)
  892. Case Else : Unexpected child, node
  893. End Select
  894. Next
  895. End Sub
  896. Sub ProcessCreateFolderElement(node, component, directory)
  897. Dim op, row, attribute, child
  898. Set row = installer.CreateRecord(UBound(CreateFolderTable))
  899. For Each attribute In node.Attributes
  900. Select Case(attribute.name)
  901. Case "op" : op = attribute.value
  902. Case "Directory" : directory = attribute.value
  903. Case Else : Unexpected attribute, node
  904. End Select
  905. Next
  906. row.StringData (CreateFolder_Directory_) = Modularize(directory)
  907. row.StringData (CreateFolder_Component_) = Modularize(component)
  908. DoAction CreateFolderTable, op, row
  909. For Each child In node.childNodes
  910. Select Case (GetElementName(child))
  911. Case Empty
  912. Case "Shortcut" : ProcessShortcutElement child, component, "[" & directory & "]"
  913. Case "Permission" : ProcessPermissionElement child, directory, "CreateFolder"
  914. Case Else : Unexpected child, node
  915. End Select
  916. Next
  917. End Sub
  918. Sub ProcessCopyFileElement(node, component, fileId)
  919. Dim op, row, attribute, value
  920. Dim table, destFile, destDir, destFileColumn, destDirColumn, sourceFile, sourceDir, bits
  921. bits = 0
  922. If IsEmpty(fileId) Then
  923. table = MoveFileTable
  924. destFileColumn = MoveFile_DestName
  925. destDirColumn = MoveFile_DestFolder
  926. For Each attribute In node.Attributes
  927. value = attribute.value
  928. Select Case(attribute.name)
  929. Case "op" : op = value
  930. Case "SourceFolder" : sourceDir = value
  931. Case "SourceName" : sourceFile = value
  932. Case "DestinationFolder" : destDir = value
  933. Case "DestinationName" : destFile = value
  934. Case "Delete" : If value = "yes" Then bits = 1
  935. Case Else : Unexpected attribute, node
  936. End Select
  937. Next
  938. Else
  939. table = DuplicateFileTable
  940. destFileColumn = DuplicateFile_DestName
  941. destDirColumn = DuplicateFile_DestFolder
  942. For Each attribute In node.Attributes
  943. value = attribute.value
  944. Select Case(attribute.name)
  945. Case "op" : op = value
  946. Case "DestinationFolder" : destDir = value
  947. Case "DestinationName" : destFile = value
  948. Case Else : Unexpected attribute, node
  949. End Select
  950. Next
  951. End If
  952. Set row = installer.CreateRecord(UBound(table))
  953. row.StringData(MoveFile_FileKey) = Modularize(ElementText(node))
  954. row.StringData(MoveFile_Component_) = Modularize(component)
  955. row.StringData(destFileColumn) = destFile
  956. row.StringData(destDirColumn) = Modularize(destDir)
  957. If IsEmpty(fileId) Then
  958. row.StringData (MoveFile_SourceName) = sourceFile
  959. row.StringData (MoveFile_SourceFolder) = Modularize(sourceDir)
  960. row.IntegerData(MoveFile_Options) = bits
  961. Else
  962. row.StringData(DuplicateFile_File_) = Modularize(fileId)
  963. End If
  964. DoAction table, op, row
  965. End Sub
  966. Sub ProcessReserveCostElement(node, component, directory)
  967. Dim op, row, attribute, value
  968. Set row = installer.CreateRecord(UBound(ReserveCostTable))
  969. For Each attribute In node.Attributes
  970. value = attribute.value
  971. Select Case(attribute.name)
  972. Case "op" : op = value
  973. Case "Directory" : directory = value
  974. Case "RunLocal" : row.StringData(ReserveCost_ReserveLocal) = CLng(value)
  975. Case "RunFromSource" : row.StringData(ReserveCost_ReserveSource) = CLng(value)
  976. Case Else : Unexpected attribute, node
  977. End Select
  978. Next
  979. row.StringData (ReserveCost_ReserveKey) = Modularize(ElementText(node)) ' !! need to auto-generate
  980. row.StringData (ReserveCost_Component_) = Modularize(component)
  981. row.StringData (ReserveCost_ReserveFolder) = Modularize(directory)
  982. DoAction ReserveCostTable, op, row
  983. End Sub
  984. Sub ProcessIsolateComponentElement(node, component)
  985. Dim op, row, attribute
  986. Set row = installer.CreateRecord(UBound(IsolatedComponentTable))
  987. For Each attribute In node.Attributes
  988. Select Case(attribute.name)
  989. Case "op" : op = attribute.value
  990. Case Else : Unexpected attribute, node
  991. End Select
  992. Next
  993. row.StringData (IsolatedComponent_Component_Shared) = Modularize(ElementText(node))
  994. row.StringData (IsolatedComponent_Component_Application) = Modularize(component)
  995. DoAction IsolatedComponentTable, op, row
  996. End Sub
  997. Sub ProcessComponentElement(node, directory, sPath)
  998. Dim op, row, attribute, value, child
  999. Dim component, keyPath, keyFound, keyPossible, bits, keyBit, keyBits, comPlusBits, condition, nDiskId
  1000. Set row = installer.CreateRecord(UBound(ComponentTable))
  1001. bits = 0
  1002. nDiskId = 0
  1003. component = ElementText(node)
  1004. For Each attribute In node.Attributes
  1005. value = attribute.value
  1006. Select Case(attribute.name)
  1007. Case "op" : op = value
  1008. Case "Id" : if value<>"" Then row.StringData(Component_ComponentId) = "{" & value & "}" Else row.StringData(Component_ComponentId) = ""
  1009. Case "SharedDllRefCount" : If value="yes" Then bits = bits Or msidbComponentAttributesSharedDllRefCount
  1010. Case "Permanent" : If value="yes" Then bits = bits Or msidbComponentAttributesPermanent
  1011. Case "Transitive" : If value="yes" Then bits = bits Or msidbComponentAttributesTransitive
  1012. Case "NeverOverwrite" : If value="yes" Then bits = bits Or msidbComponentAttributesNeverOverwrite
  1013. Case "KeyPath" : If value="yes" Then ' Directory_ is KeyPath
  1014. keyFound = "yes"
  1015. keyPath = directory
  1016. keyBits = keyBit
  1017. End If
  1018. Case "ComPlusFlags" : comPlusBits = CInt(value)
  1019. Case "Location"
  1020. If value="source" Then
  1021. bits = bits Or msidbComponentAttributesSourceOnly
  1022. ElseIf value="either" Then
  1023. bits = bits Or msidbComponentAttributesOptional
  1024. End If
  1025. Case "DiskId" : nDiskId = value
  1026. Case Else : Unexpected attribute, node
  1027. End Select
  1028. Next
  1029. If IsEmpty(directory) And op <> "exist" Then Fail "Root Component element op must be 'exist'"
  1030. For Each child In node.childNodes
  1031. keyPossible = "no"
  1032. Select Case (GetElementName(child))
  1033. Case Empty
  1034. Case "File" : keyPossible = ProcessFileElement (child, component, (nDiskId), sPath) : keyBit = 0
  1035. Case "Registry" : keyPossible = ProcessRegistryElement(child, component) : keyBit = msidbComponentAttributesRegistryKeyPath
  1036. Case "ODBCDataSource" : keyPossible = ProcessODBCDataSource (child, component, Empty) : keyBit = msidbComponentAttributesODBCDataSource
  1037. Case "ODBCDriver" : ProcessODBCDriver child, component, Empty, ODBCDriverTable
  1038. Case "ODBCTranslator" : ProcessODBCDriver child, component, Empty, ODBCTranslatorTable
  1039. Case "TypeLib" : Call ProcessTypeLibElement (child, component, Empty)
  1040. Case "Shortcut" : ProcessShortcutElement child, component, "[" & directory & "]"
  1041. Case "IniFile" : ProcessIniElement child, component
  1042. Case "CreateFolder" : ProcessCreateFolderElement child, component, (directory)
  1043. Case "CopyFile" : ProcessCopyFileElement child, component, Empty
  1044. Case "IsolateComponent" : ProcessIsolateComponentElement child, component
  1045. Case "ReserveCost" : ProcessReserveCostElement child, component, (directory)
  1046. Case "RemoveFile" : ProcessRemoveFileElement child, component, (directory)
  1047. Case "Environment" : ProcessEnvironmentElement child, component
  1048. Case "ServiceControl" : ProcessServiceControlElement child, component
  1049. Case "ServiceInstall" : ProcessServiceInstallElement child, component
  1050. Case "Class" : ProcessClassElement child, component, Empty ' no feature, no advertise
  1051. Case "Condition"
  1052. If Not IsEmpty(condition) Then Fail "Can only have one Condition for a component"
  1053. condition = ElementText(child)
  1054. '!! need to make sure no attributes
  1055. Case Else : Unexpected child, node
  1056. End Select
  1057. If keyPossible = "yes" And keyFound = "yes" Then
  1058. Fail "Component has more than one KeyPath: " & component
  1059. ElseIf keyPossible = "yes" Or (keyFound = Empty And keyPossible <> "no") Then
  1060. keyFound = keyPossible
  1061. keyPath = ElementText(child)
  1062. keyBits = keyBit
  1063. End If
  1064. Next
  1065. If keyFound = "no" Or keyFound = "noreg" Then Fail "Component has no KeyPath element and there is more than one choice: " & component
  1066. row.StringData (Component_Component) = Modularize(component)
  1067. row.StringData (Component_Directory_) = Modularize(directory)
  1068. row.StringData (Component_Condition) = condition
  1069. row.IntegerData(Component_Attributes) = bits Or keyBits
  1070. row.StringData (Component_KeyPath) = Modularize(keyPath)
  1071. DoAction ComponentTable, op, row
  1072. If Not IsEmpty(comPlusBits) Then
  1073. row.ClearData
  1074. row.StringData (Complus_Component_) = component
  1075. row.IntegerData(Complus_ExpType) = comPlusBits
  1076. DoAction ComplusTable, op, row
  1077. End If
  1078. If fModule Then
  1079. Set row = installer.CreateRecord(UBound(ModuleComponentsTable))
  1080. row.StringData (ModuleComponents_Component)= Modularize(component)
  1081. row.StringData (ModuleComponents_ModuleID) = ModularizeX(productName)
  1082. row.IntegerData(ModuleComponents_Language) = CInt(productLanguage)
  1083. DoAction ModuleComponentsTable, op, row
  1084. End If
  1085. End Sub
  1086. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  1087. ' ProcessDirectoryModuleElement
  1088. Sub ProcessDirectoryModuleElement(node, directory)
  1089. Dim attribute, value
  1090. Dim sName, nLang, src, nDiskId
  1091. Dim aModuleData(5)
  1092. ' not valid in schema, but double check anyway
  1093. If fModule Then Fail "Cannot specify a Module inside a Module"
  1094. If IsEmpty(directory) Then Fail "Module must be found under a Directory element"
  1095. sName = ElementText(node)
  1096. nDiskId = 0
  1097. nLang = 0 ' default language is "neutral"
  1098. For Each attribute In node.Attributes
  1099. value = attribute.value
  1100. Select Case(attribute.name)
  1101. Case "op" : Fail "robmen - ProcessDirectoryModuleElement() - op attribute currently unsupported in this context"
  1102. Case "Language" : nLang = CInt(value)
  1103. Case "DiskId" : nDiskId = value
  1104. Case "src" : src = value
  1105. Case Else : Unexpected attribute, node
  1106. End Select
  1107. Next
  1108. If Not fModule And 0 = nDiskId Then Fail "Must specify a DiskId for Module: " & sName
  1109. If IsEmpty(src) Then Fail "Must specify a 'src' for every Module"
  1110. ' if the Module was already defined in Feature tree
  1111. If dictModules.Exists(sName) Then
  1112. Dim aData
  1113. aData = dictModules.Item(sName)
  1114. If IsEmpty(aData(0)) And IsEmpty(aData(2)) And IsEmpty(aData(3)) Then
  1115. aData(0) = src
  1116. aData(1) = nDiskId
  1117. aData(2) = nLang
  1118. If "targetdir" = LCase(directory) Then aData(3) = Empty Else aData(3) = directory
  1119. 'WScript.Echo "Y Directory: " & aData(3)
  1120. dictModules.Item(sName) = aData
  1121. Else
  1122. Fail "Cannot merge same Module twice: " & sName
  1123. End If
  1124. Else
  1125. aModuleData(0) = src
  1126. aModuleData(1) = nDiskId
  1127. aModuleData(2) = nLang
  1128. If "targetdir" = LCase(directory) Then aModuleData(3) = Empty Else aModuleData(3) = directory
  1129. aModuleData(4) = Empty ' no primary Feature yet
  1130. aModuleData(5) = Empty ' no secondary Features yet
  1131. 'WScript.Echo "Z Directory: " & aModuleData(3)
  1132. dictModules.Add sName, aModuleData
  1133. End If
  1134. End Sub ' ProcessDirectoryModuleElement
  1135. Sub ProcessFeatureElement(node, parent, lastDisplay)
  1136. Dim child, value, attribute, row, bits, op, feature, display, childDisplay
  1137. Set row = installer.CreateRecord(UBound(FeatureTable))
  1138. bits = 0
  1139. If fModule Then
  1140. feature = "{00000000-0000-0000-0000-000000000000}"
  1141. Else
  1142. feature = ElementText(node)
  1143. For Each attribute In node.Attributes
  1144. value = attribute.value
  1145. Select Case(attribute.name)
  1146. Case "op" : op = value
  1147. Case "Display" : display = value
  1148. Case "Title" : row.StringData (Feature_Title) = value
  1149. Case "Description" : row.StringData (Feature_Description) = value
  1150. Case "Level" : row.IntegerData(Feature_Level) = CInt(value)
  1151. Case "ConfigurableDirectory" : row.StringData (Feature_Directory_) = value
  1152. Case "InstallDefault" : If value="source" Then bits = bits Or msidbFeatureAttributesFavorSource
  1153. Case "TypicalDefault" : If value="advertise" Then bits = bits Or msidbFeatureAttributesFavorAdvertise
  1154. Case "FollowParent" : If value="yes" Then bits = bits Or msidbFeatureAttributesFollowParent
  1155. Case "Absent" : If value="disallow" Then bits = bits Or msidbFeatureAttributesUIDisallowAbsent
  1156. Case "AllowAdvertise"
  1157. If value="no" Then
  1158. bits = bits Or msidbFeatureAttributesDisallowAdvertise
  1159. ElseIf value="system" Then
  1160. bits = bits Or msidbFeatureAttributesNoUnsupportedAdvertise
  1161. End If
  1162. Case Else : Unexpected attribute, node
  1163. End Select
  1164. Next
  1165. If IsEmpty(display) Then display = "collapse"
  1166. Select Case(display)
  1167. Case "hidden" : display = 0
  1168. Case "expand" : lastDisplay = (lastDisplay + 1) Or 1 : display = lastDisplay
  1169. Case "collapse" : lastDisplay = (lastDisplay Or 1) + 1 : display = lastDisplay
  1170. Case Else : Fail "Unexpected Feature Display value: " & display
  1171. End Select
  1172. row.StringData (Feature_Feature) = feature
  1173. row.StringData (Feature_Feature_Parent) = parent
  1174. row.IntegerData(Feature_Attributes) = bits
  1175. row.IntegerData(Feature_Display) = display
  1176. DoAction FeatureTable, op, row
  1177. End If
  1178. childDisplay = 0
  1179. For Each child In node.childNodes
  1180. Select Case (GetElementName(child))
  1181. Case Empty
  1182. Case "Feature" : ProcessFeatureElement child, feature, childDisplay
  1183. Case "Condition" : ProcessFeatureCondition child, feature
  1184. Case "Module" : ProcessFeatureModule child, feature
  1185. Case "Component" : ProcessFeatureComponent child, feature
  1186. Case Else : Unexpected child, node
  1187. End Select
  1188. Next
  1189. End Sub
  1190. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  1191. ' ProcessFeatureCondition
  1192. Sub ProcessFeatureCondition(node, feature)
  1193. Dim attribute, child, row, op, level
  1194. Set row = installer.CreateRecord(UBound(ConditionTable))
  1195. For Each attribute In node.Attributes
  1196. Select Case(attribute.name)
  1197. Case "op" : op = attribute.value
  1198. Case "Level" : level = CInt(attribute.value)
  1199. Case Else : Unexpected attribute, node
  1200. End Select
  1201. Next
  1202. row.StringData (Condition_Feature_) = feature
  1203. row.IntegerData(Condition_Level) = level
  1204. row.StringData (Condition_Condition) = ElementText(node)
  1205. DoAction ConditionTable, op, row
  1206. End Sub ' ProcessFeatureCondition
  1207. Sub ProcessFeatureComponent(node, feature)
  1208. Dim child, component, attribute, row, op
  1209. Set row = installer.CreateRecord(UBound(FeatureComponentsTable))
  1210. component = ElementText(node)
  1211. For Each attribute In node.Attributes
  1212. Select Case(attribute.name)
  1213. Case "op" : op = attribute.value
  1214. Case "Location" ' ignore default value passed by XML parser
  1215. Case Else : Unexpected attribute, node
  1216. End Select
  1217. Next
  1218. For Each child In node.childNodes
  1219. Select Case (GetElementName(child))
  1220. Case Empty
  1221. Case "Category" : ProcessCategoryElement child, component, feature
  1222. Case "Class" : ProcessClassElement child, component, feature
  1223. Case "Shortcut" : ProcessShortcutElement child, component, (feature)
  1224. Case "Extension" : ProcessExtensionElement child, component, feature, Empty
  1225. Case "ProgId" : ProcessProgIdElement child, component, feature, Empty, Empty, Empty
  1226. Case "Assembly" : ProcessAssemblyElement child, component, feature
  1227. Case Else : Unexpected child, node
  1228. End Select
  1229. Next
  1230. If IsEmpty(component) Then Fail "Missing Component key name"
  1231. If Not fModule Then
  1232. row.StringData (FeatureComponents_Feature_) = feature
  1233. row.StringData (FeatureComponents_Component_) = component
  1234. DoAction FeatureComponentsTable, op, row
  1235. End If
  1236. End Sub
  1237. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  1238. ' ProcessFeatureModule
  1239. Sub ProcessFeatureModule(node, feature)
  1240. Dim attribute
  1241. Dim sName, fPrimary
  1242. Dim aModuleData(5)
  1243. ' not valid in schema, but double check anyway
  1244. If fModule Then Fail "Cannot specify a Module inside a Module"
  1245. sName = ElementText(node)
  1246. For Each attribute In node.Attributes
  1247. Select Case(attribute.name)
  1248. Case "op" : Fail "robmen - ProcessFeatureModule() - op attribute currently unsupported in this context"
  1249. Case "Primary" : If "yes" = attribute.value Then fPrimary = True
  1250. Case Else : Unexpected attribute, node
  1251. End Select
  1252. Next
  1253. ' if the Module was already defined in Directory or Feature tree
  1254. If dictModules.Exists(sName) Then
  1255. Dim aData
  1256. aData = dictModules.Item(sName)
  1257. If fPrimary Then
  1258. If Not IsEmpty(aData(4)) Then Fail "Cannot specify two 'primary' Features for Module: " & sName
  1259. aData(4) = feature
  1260. If 0 = Len(aData(5)) Then aData(5) = Empty
  1261. Else ' not the primary feature
  1262. If 0 = Len(aData(5)) Then
  1263. aData(5) = feature
  1264. Else
  1265. aData(5) = aData(5) & ":" & feature
  1266. End If
  1267. End If
  1268. 'WScript.Echo "A Directory: " & aData(3)
  1269. dictModules.Item(sName) = aData
  1270. Else
  1271. aModuleData(0) = Empty
  1272. aModuleData(1) = 0 ' invalid DiskId
  1273. aModuleData(2) = Empty
  1274. aModuleData(3) = Empty
  1275. If fPrimary Then
  1276. aModuleData(4) = feature
  1277. If 0 = Len(aModuleData(5)) Then aModuleData(5) = Empty
  1278. Else
  1279. aModuleData(4) = Empty
  1280. If 0 = Len(aModuleData(5)) Then
  1281. aModuleData(5) = feature
  1282. Else
  1283. aModuleData(5) = aModuleData(5) & ":" & feature
  1284. End If
  1285. End If
  1286. 'WScript.Echo "B Directory: " & aModuleData(3)
  1287. dictModules.Add sName, aModuleData
  1288. End If
  1289. End Sub ' ProcessFeatureModule
  1290. Function ProcessFileElement(node, component, nDiskId, sPath)
  1291. Dim op, value, attribute, child, row, bits, fileId, bindPath, selfRegCost, shortName, longName, nFileSize, fontTitle, nSequence, src
  1292. Dim aData(1)
  1293. Set row = installer.CreateRecord(UBound(FileTable))
  1294. nFileSize = 0
  1295. bits = 0
  1296. nSequence = Empty
  1297. ProcessFileElement = "file"
  1298. fileId = ElementText(node)
  1299. For Each attribute In node.Attributes
  1300. value = attribute.value
  1301. Select Case(attribute.name)
  1302. Case "op" : op = value
  1303. Case "Name" : shortName = value
  1304. Case "LongName" : longName = value
  1305. Case "FileSize" : nFileSize = CLng(value)
  1306. Case "Version" : row.StringData (File_Version) = value
  1307. Case "Language" : row.StringData (File_Language) = value
  1308. Case "Sequence" : nSequence = CInt(value)
  1309. Case "ReadOnly" : If value="yes" Then bits = bits Or msidbFileAttributesReadOnly
  1310. Case "Hidden" : If value="yes" Then bits = bits Or msidbFileAttributesHidden
  1311. Case "System" : If value="yes" Then bits = bits Or msidbFileAttributesSystem
  1312. Case "Vital" : If value="yes" Then bits = bits Or msidbFileAttributesVital
  1313. Case "Checksum" : If value="yes" Then bits = bits Or msidbFileAttributesChecksum
  1314. Case "PatchAdded" : If value="yes" Then bits = bits Or msidbFileAttributesPatchAdded
  1315. Case "Noncompressed" : If value="yes" Then bits = bits Or msidbFileAttributesNoncompressed
  1316. Case "Compressed" : If value="yes" Then bits = bits Or msidbFileAttributesCompressed
  1317. Case "KeyPath" : ProcessFileElement = value
  1318. Case "BindPath" : bindPath = value
  1319. Case "SelfRegCost" : selfRegCost = value
  1320. Case "TrueType" : If value="yes" Then fontTitle = ""
  1321. Case "FontTitle" : fontTitle = value
  1322. Case "DiskId" : nDiskId = value
  1323. Case "src" : src = value ' BaseDir() is called later, not now
  1324. Case Else : Unexpected attribute, node
  1325. End Select
  1326. Next
  1327. If IsEmpty(nSequence) Then nSequence = fileSeq : fileSeq = fileSeq + 1 Else fileSeq = nSequence + 1
  1328. If fModule And 0 <> nDiskId Then Fail "Cannot specify a DiskId when compiling a Module" ' not allowed by schema but check anyway
  1329. If Not fModule And 0 = nDiskId Then Fail "Must specify a DiskId for File: " & fileId
  1330. If Not IsEmpty(bindPath) Then ProcessBindImage fileId, bindPath, op
  1331. If Not IsEmpty(selfRegCost) Then ProcessSelfReg fileId, selfregCost, op
  1332. If Not IsEmpty(fontTitle) Then ProcessFont fileId, fontTitle, op
  1333. row.StringData(File_File) = Modularize(fileId)
  1334. row.StringData(File_Component_) = Modularize(component)
  1335. If IsEmpty(longName) Then row.StringData(File_FileName) = shortName Else row.StringData(File_FileName) = shortName & "|" & longName
  1336. row.IntegerData(File_FileSize) = nFileSize
  1337. row.IntegerData(File_Attributes) = bits
  1338. row.IntegerData(File_Sequence) = nSequence
  1339. DoAction FileTable, op, row
  1340. ' add to build dictionary
  1341. If 0 < Len(src) Then
  1342. If "\" = Right(src, 1) Then
  1343. If IsEmpty(longName) Then src = src & shortName Else src = src & longName
  1344. End If
  1345. aData(0) = src
  1346. Else
  1347. If IsEmpty(longName) Then aData(0) = sPath & shortName Else aData(0) = sPath & longName
  1348. End If
  1349. aData(1) = nDiskId
  1350. dictFiles.Add Modularize(fileId), aData
  1351. For Each child In node.childNodes
  1352. Select Case (GetElementName(child))
  1353. Case Empty
  1354. Case "Shortcut" : ProcessShortcutElement child, component, "[#" & fileId & "]"
  1355. Case "CopyFile" : ProcessCopyFileElement child, component, Modularize(fileId)
  1356. Case "ODBCDriver" : ProcessODBCDriver child, component, Modularize(fileId), ODBCDriverTable
  1357. Case "ODBCTranslator" : ProcessODBCDriver child, component, Modularize(fileId), ODBCTranslatorTable
  1358. Case "Permission" : ProcessPermissionElement child, Modularize(fileId), "File"
  1359. Case Else : Unexpected child, node
  1360. End Select
  1361. Next
  1362. End Function
  1363. Sub ProcessBindImage(file, path, op)
  1364. Dim row : Set row = installer.CreateRecord(UBound(BindImageTable))
  1365. row.StringData (BindImage_File_) = Modularize(file)
  1366. row.StringData (BindImage_Path) = ModularizeProperty(path)
  1367. DoAction BindImageTable, op, row
  1368. End Sub
  1369. Sub ProcessSelfReg(file, cost, op)
  1370. Dim row : Set row = installer.CreateRecord(UBound(SelfRegTable))
  1371. row.StringData (SelfReg_File_) = Modularize(file)
  1372. row.StringData (SelfReg_Cost) = cost
  1373. DoAction SelfRegTable, op, row
  1374. End Sub
  1375. Sub ProcessFont(file, fontTitle, op)
  1376. Dim row : Set row = installer.CreateRecord(UBound(FontTable))
  1377. row.StringData (Font_File_) = Modularize(file)
  1378. row.StringData (Font_FontTitle) = fontTitle
  1379. DoAction FontTable, op, row
  1380. End Sub
  1381. Sub ProcessRemoveFileElement(node, component, directory)
  1382. Dim op, row, attribute, value
  1383. Set row = installer.CreateRecord(UBound(RemoveFileTable))
  1384. For Each attribute In node.Attributes
  1385. value = attribute.value
  1386. Select Case(attribute.name)
  1387. Case "op" : op = value
  1388. Case "Directory" : directory = value
  1389. Case "Name" : row.StringData(RemoveFile_FileName) = value
  1390. Case "On"
  1391. Select Case(value)
  1392. Case "install" : value = 1
  1393. Case "uninstall" : value = 2
  1394. Case "both" : value = 3
  1395. Case Else : Fail "Unexpected value for RemoveFile 'On' attribute: " & value
  1396. End Select
  1397. row.IntegerData(RemoveFile_InstallMode) = value
  1398. Case Else : Unexpected attribute, node
  1399. End Select
  1400. Next
  1401. row.StringData (RemoveFile_FileKey) = Modularize(ElementText(node)) ' !! need to auto-generate
  1402. row.StringData (RemoveFile_Component_) = Modularize(component)
  1403. row.StringData (RemoveFile_DirProperty) = Modularize(directory)
  1404. DoAction RemoveFileTable, op, row
  1405. End Sub
  1406. Sub ProcessPermissionElement(node, tableKey, tableName)
  1407. Dim value, attribute, row, op, source, target, bit, bits, specialPermissions
  1408. Set row = installer.CreateRecord(UBound(LockPermissionsTable))
  1409. bits = CLng(0)
  1410. Select Case(tableName)
  1411. Case "File" : specialPermissions = filePermissions
  1412. Case "CreateFolder" : specialPermissions = folderPermissions
  1413. Case "Registry" : specialPermissions = registryPermissions
  1414. Case Else : Fail "Invalid parent element type for Permission: " & tableName
  1415. End Select
  1416. For Each attribute In node.Attributes
  1417. value = attribute.value
  1418. Select Case(attribute.name)
  1419. Case "op" : op = value
  1420. Case "Domain" : row.StringData(LockPermissions_Domain) = value
  1421. Case "User" : row.StringData(LockPermissions_User) = value
  1422. Case Else
  1423. bit = NameToBit(standardPermissions, attribute.name, value)
  1424. If Not IsEmpty(bit) Then
  1425. bit = bit * 65536
  1426. Else
  1427. bit = NameToBit(genericPermissions, attribute.name, value)
  1428. If Not IsEmpty(bit) Then
  1429. If bit = 8 Then bit = &h80000000 Else bit = bit * &h10000000
  1430. Else
  1431. bit = NameToBit(specialPermissions, attribute.name, value)
  1432. If IsEmpty(bit) Then Unexpected attribute, node
  1433. End If
  1434. End If
  1435. bits = bits Or bit
  1436. End Select
  1437. Next
  1438. row.StringData (LockPermissions_LockObject) = Modularize(tableKey)
  1439. row.StringData (LockPermissions_Table) = tableName
  1440. row.IntegerData(LockPermissions_Permission) = bits
  1441. DoAction LockPermissionsTable, op, row
  1442. End Sub
  1443. Sub ProcessCategoryElement(node, component, feature)
  1444. Dim value, attribute, child, row, op
  1445. Set row = installer.CreateRecord(UBound(PublishComponentTable))
  1446. For Each attribute In node.Attributes
  1447. value = attribute.value
  1448. Select Case(attribute.name)
  1449. Case "op" : op = value
  1450. Case "Qualifier" : row.StringData (PublishComponent_Qualifier) = value
  1451. Case "AppData" : row.StringData (PublishComponent_AppData) = value
  1452. Case Else : Unexpected attribute, node
  1453. End Select
  1454. Next
  1455. row.StringData(PublishComponent_ComponentId) = ElementText(node)
  1456. row.StringData(PublishComponent_Component_) = Modularize(component)
  1457. row.StringData(PublishComponent_Feature_) = feature
  1458. DoAction PublishComponentTable, op, row
  1459. End Sub
  1460. Sub ProcessShortcutElement(node, component, target)
  1461. Dim value, attribute, child, row, op, shortName, longName
  1462. Set row = installer.CreateRecord(UBound(ShortcutTable))
  1463. For Each attribute In node.Attributes
  1464. value = attribute.value
  1465. Select Case(attribute.name)
  1466. Case "op" : op = value
  1467. Case "Target" : target = value
  1468. Case "Name" : shortName = value
  1469. Case "LongName" : longName = value
  1470. Case "Directory" : row.StringData (Shortcut_Directory_) = Modularize(value)
  1471. Case "Description" : row.StringData (Shortcut_Description) = value
  1472. Case "Arguments" : row.StringData (Shortcut_Arguments) = value
  1473. Case "Hotkey" : row.IntegerData(Shortcut_Hotkey) = CInt(value)
  1474. Case "Icon" : row.StringData (Shortcut_Icon_) = value
  1475. Case "IconIndex" : row.IntegerData(Shortcut_IconIndex) = CInt(value)
  1476. Case "Show" : If value = "normal" Then row.IntegerData(Shortcut_ShowCmd) = 1 Else If value = "maximized" Then row.IntegerData(Shortcut_ShowCmd) = 3 Else If value = "minimized" Then row.IntegerData(Shortcut_ShowCmd) = 7
  1477. Case "WorkingDirectory" : row.StringData (Shortcut_WkDir) = value
  1478. Case Else : Unexpected attribute, node
  1479. End Select
  1480. Next
  1481. row.StringData(Shortcut_Shortcut) = Modularize(ElementText(node))
  1482. row.StringData(Shortcut_Component_) = Modularize(component)
  1483. row.StringData(Shortcut_Target) = ModularizeProperty(target)
  1484. If Not IsEmpty(longName) Then shortName = shortName & "|" & longName
  1485. row.StringData(Shortcut_Name) = shortName
  1486. DoAction ShortcutTable, op, row
  1487. End Sub
  1488. Sub ProcessIniElement(node, component)
  1489. Dim value, attribute, row, op, table, action
  1490. Set row = installer.CreateRecord(UBound(IniFileTable))
  1491. For Each attribute In node.Attributes
  1492. value = attribute.value
  1493. Select Case(attribute.name)
  1494. Case "op" : op = value
  1495. Case "Action" : action = value
  1496. Case "Name" : row.StringData (IniFile_FileName) = value
  1497. Case "Directory" : row.StringData (IniFile_DirProperty) = Modularize(value)
  1498. Case "Section" : row.StringData (IniFile_Section) = value
  1499. Case "Key" : row.StringData (IniFile_Key) = value
  1500. Case "Value" : row.StringData (IniFile_Value) = value
  1501. Case Else : Unexpected attribute, node
  1502. End Select
  1503. Next
  1504. Select Case(action)
  1505. Case "addLine" : action = msidbIniFileActionAddLine
  1506. Case "createLine" : action = msidbIniFileActionCreateLine
  1507. Case "addTag" : action = msidbIniFileActionAddTag
  1508. Case "removeLine" : action = msidbIniFileActionRemoveLine
  1509. Case "removeTag" : action = msidbIniFileActionRemoveTag
  1510. Case Else : Fail "Unexpected IniFile action: " & action
  1511. End Select
  1512. row.StringData (IniFile_IniFile) = Modularize(ElementText(node)) '!!! auto-generate?
  1513. If action = msidbIniFileActionRemoveLine Or action = msidbIniFileActionRemoveTag Then
  1514. table = RemoveIniFileTable
  1515. Else
  1516. table = IniFileTable
  1517. End If
  1518. DoAction table, op, row
  1519. End Sub
  1520. Sub ProcessEnvironmentElement(node, component)
  1521. Dim op, row, attribute, value, text, system, name, action, uninstall, separator, part
  1522. Set row = installer.CreateRecord(UBound(EnvironmentTable))
  1523. uninstall = "-" ' default to remove at uninstall
  1524. For Each attribute In node.Attributes
  1525. value = attribute.value
  1526. Select Case(attribute.name)
  1527. Case "op" : op = value
  1528. Case "Name" : name = value
  1529. Case "Value" : text = value
  1530. Case "Separator" : separator = value
  1531. Case "Part" : part = value
  1532. Case "System" : If value = "yes" Then system = "*"
  1533. Case "Permanent" : If value = "yes" Then uninstall = Empty
  1534. Case "Action"
  1535. Select Case(value)
  1536. Case "create" : action = "="
  1537. Case "set" : action = "+"
  1538. Case "remove" : action = "!"
  1539. Case Else : Fail "Unexpected Environment Action value: " & value
  1540. End Select
  1541. End Select
  1542. Next
  1543. Select Case(part)
  1544. Case Empty
  1545. Case "all"
  1546. Case "first" : text = text & delim & "[~]"
  1547. Case "last" : text = "[~]" & delim & text
  1548. Case Else : Fail "Unexpected Environment Part value: " & part
  1549. End Select
  1550. row.StringData (Environment_Environment) = Modularize(ElementText(node)) ' !! need to auto-generate
  1551. row.StringData (Environment_Component_) = Modularize(component)
  1552. row.StringData (Environment_Name) = action & uninstall & system & name
  1553. row.StringData (Environment_Value) = text
  1554. DoAction EnvironmentTable, op, row
  1555. End Sub
  1556. Sub ProcessServiceControlElement(node, component)
  1557. Dim child, op, row, attribute, value, name, events, wait, arguments
  1558. Set row = installer.CreateRecord(UBound(ServiceControlTable))
  1559. events = 0 ' default do nothing
  1560. wait = ""
  1561. For Each attribute In node.Attributes
  1562. value = attribute.value
  1563. Select Case(attribute.name)
  1564. Case "op" : op = value
  1565. Case "Name" : name = value
  1566. Case "Start"
  1567. Select Case(value)
  1568. Case "install" : events = events Or msidbServiceControlEventStart
  1569. Case "uninstall" : events = events Or msidbServiceControlEventUninstallStart
  1570. Case "both" : events = events Or msidbServiceControlEventStart Or msidbServiceControlEventUninstallStart
  1571. Case Else : Fail "Unknown Service start type: " & value
  1572. End Select
  1573. Case "Stop"
  1574. Select Case(value)
  1575. Case "install" : events = events Or msidbServiceControlEventStop
  1576. Case "uninstall" : events = events Or msidbServiceControlEventUninstallStop
  1577. Case "both" : events = events Or msidbServiceControlEventStop Or msidbServiceControlEventUninstallStop
  1578. Case Else : Fail "Unknown Service stop type: " & value
  1579. End Select
  1580. Case "Remove"
  1581. Select Case(value)
  1582. Case "install" : events = events Or msidbServiceControlEventRemove
  1583. Case "uninstall" : events = events Or msidbServiceControlEventUninstallRemove
  1584. Case "both" : events = events Or msidbServiceControlEventRemove Or msidbServiceControlEventUninstallRemove
  1585. Case Else : Fail "Unknown Service remove type: " & value
  1586. End Select
  1587. Case "Wait"
  1588. Select Case(value)
  1589. Case "yes" : wait = "1" ' strings used since integer column is nullable
  1590. Case "no" : wait = "0"
  1591. Case Else : Fail "Unknown Wait value: " & value
  1592. End Select
  1593. End Select
  1594. Next
  1595. ' get the ServiceControl arguments
  1596. arguments = ""
  1597. For Each child In node.childNodes
  1598. Select Case (GetElementName(child))
  1599. Case Empty
  1600. Case "ServiceArgument"
  1601. If Len(arguments) > 0 Then arguments = arguments & "[~]"
  1602. arguments = arguments & ElementText(child)
  1603. Case Else : Unexpected child, node
  1604. End Select
  1605. Next
  1606. row.StringData (ServiceControl_ServiceControl) = Modularize(ElementText(node)) ' !! need to auto-generate
  1607. row.StringData (ServiceControl_Name) = name
  1608. row.IntegerData(ServiceControl_Event) = events
  1609. row.StringData (ServiceControl_Arguments) = arguments
  1610. row.StringData (ServiceControl_Wait) = wait
  1611. row.StringData (ServiceControl_Component_) = Modularize(component)
  1612. DoAction ServiceControlTable, op, row
  1613. End Sub ' ProcessServiceControlElement
  1614. Sub ProcessServiceInstallElement(node, component)
  1615. Dim child, op, row, attribute, value, typebits, errorbits, erasedesc, dependencies
  1616. Set row = installer.CreateRecord(UBound(ServiceInstallTable))
  1617. typebits = 0
  1618. errorbits = 0
  1619. erasedesc = False ' don't erase the description
  1620. For Each attribute In node.Attributes
  1621. value = attribute.value
  1622. Select Case(attribute.name)
  1623. Case "op" : op = value
  1624. Case "Name" : row.StringData (ServiceInstall_Name) = value
  1625. Case "DisplayName" : row.StringData (ServiceInstall_DisplayName) = value
  1626. Case "Type"
  1627. Select Case(value)
  1628. Case "ownProcess" : typebits = typebits Or msidbServiceInstallOwnProcess
  1629. Case "shareProcess" : typebits = typebits Or msidbServiceInstallShareProcess
  1630. Case "kernelDriver" : Fail "Service type not currently supported by the Windows Installer: " & value ' = 1
  1631. Case "systemDriver" : Fail "Service type not currently supported by the Windows Installer: " & value ' = 2
  1632. Case Else : Fail "Unknown Service type: " & value
  1633. End Select
  1634. Case "Interactive" : If "yes" = value Then typebits = typebits Or msidbServiceInstallInteractive
  1635. Case "Start"
  1636. Select Case(value)
  1637. Case "auto" : row.IntegerData(ServiceInstall_StartType) = msidbServiceInstallAutoStart
  1638. Case "demand" : row.IntegerData(ServiceInstall_StartType) = msidbServiceInstallDemandStart
  1639. Case "disabled" : row.IntegerData(ServiceInstall_StartType) = msidbServiceInstallDisabled
  1640. Case "boot" : Fail "Service start type not currently supported by the Windows Installer: " & value ' = 0
  1641. Case "system" : Fail "Service start type not currently supported by the Windows Installer: " & value ' = 1
  1642. Case Else : Fail "Unknown Service start type: " & value
  1643. End Select
  1644. Case "ErrorControl"
  1645. Select Case(value)
  1646. Case "ignore" : errorbits = errorbits Or msidbServiceInstallErrorIgnore
  1647. Case "normal" : errorbits = errorbits Or msidbServiceInstallErrorNormal
  1648. Case "critical" : errorbits = errorbits Or msidbServiceInstallErrorCritical
  1649. Case Else : Fail "Unknown Service error control type: " & value
  1650. End Select
  1651. Case "Vital" : If "yes" = value Then errorbits = errorbits Or msidbServiceInstallErrorControlVital
  1652. Case "LocalGroup" : row.StringData (ServiceInstall_LoadOrderGroup) = value
  1653. Case "Account" : row.StringData (ServiceInstall_StartName) = value
  1654. Case "Password" : row.StringData (ServiceInstall_Password) = value
  1655. Case "Arguments" : row.StringData (ServiceInstall_Arguments) = value
  1656. Case "Description" : row.StringData (ServiceInstall_Description) = value
  1657. Case "EraseDescription": If "yes"=value Then erasedesc = True
  1658. End Select
  1659. Next
  1660. If erasedesc Then row.StringData (ServiceInstall_Description) = "[~]"
  1661. ' get the ServiceInstall dependencies
  1662. dependencies = ""
  1663. For Each child In node.childNodes
  1664. Select Case (GetElementName(child))
  1665. Case Empty
  1666. Case "ServiceDependency"
  1667. ' If Len(dependencies) > 0 Then dependencies = dependencies & "[~]" ' !!! ??? If two [~] are not necessary on the end of Dependencies
  1668. ' dependencies = dependencies & ProcessServiceDependency(child) ' uncomment this code and clean up the lines below
  1669. dependencies = dependencies & ProcessServiceDependency(child) & "[~]"
  1670. Case Else : Unexpected child, node
  1671. End Select
  1672. Next
  1673. If Len(dependencies) > 0 Then dependencies = dependencies & "[~]"
  1674. row.StringData (ServiceInstall_ServiceInstall) = Modularize(ElementText(node)) ' !! need to auto-generate
  1675. row.IntegerData(ServiceInstall_ServiceType) = typebits
  1676. row.IntegerData(ServiceInstall_ErrorControl) = errorbits
  1677. row.StringData (ServiceInstall_Dependencies) = dependencies
  1678. row.StringData (ServiceInstall_Component_) = Modularize(component)
  1679. DoAction ServiceInstallTable, op, row
  1680. End Sub ' ProcessServiceInstallElement
  1681. Function ProcessServiceDependency(node)
  1682. Dim attribute, value
  1683. ProcessServiceDependency = ElementText(node)
  1684. For Each attribute In node.Attributes
  1685. value = attribute.value
  1686. Select Case(attribute.name)
  1687. Case "Group" If "yes"=value Then ProcessServiceDependency = "+" & ProcessServiceDependency
  1688. End Select
  1689. Next
  1690. End Function ' ProcessServiceDependency
  1691. Sub ProcessRegistry(id, root, key, name, data, component, op)
  1692. ' if no id was provided, create one
  1693. If IsEmpty(id) Then id = "r" & regCount : regCount = regCount + 1
  1694. Dim row : Set row = installer.CreateRecord(UBound(RegistryTable))
  1695. row.StringData (Registry_Registry) = Modularize(id)
  1696. row.StringData (Registry_Component_) = Modularize(component)
  1697. row.IntegerData(Registry_Root) = root
  1698. row.StringData (Registry_Key) = ModularizeProperty(key)
  1699. row.StringData (Registry_Name) = ModularizeProperty(name)
  1700. row.StringData (Registry_Value) = ModularizeProperty(data)
  1701. DoAction RegistryTable, op, row
  1702. End Sub
  1703. Function ProcessRegistryElement(node, component)
  1704. Dim value, attribute, row, op, child, root, key, name, data, action
  1705. ProcessRegistryElement = "reg"
  1706. For Each attribute In node.Attributes
  1707. value = attribute.value
  1708. Select Case(attribute.name)
  1709. Case "op" : op = value
  1710. Case "Key" : key = value
  1711. Case "Name" : name = value
  1712. Case "Value" : data = value
  1713. Case "Action" : action = value
  1714. Case "KeyPath" : ProcessRegistryElement = value
  1715. Case "Root"
  1716. Select Case(value)
  1717. Case "HKMU" : root = -1
  1718. Case "HKCR" : root = 0
  1719. Case "HKCU" : root = 1
  1720. Case "HKLM" : root = 2
  1721. Case "HKU" : root = 3
  1722. Case Else : Fail "Unknown Registry root type: " & value
  1723. End Select
  1724. Case Else : Unexpected attribute, node
  1725. End Select
  1726. Next
  1727. Select Case(action)
  1728. Case Empty
  1729. Case "write" : action = Empty
  1730. Case "remove" : action = name
  1731. Case "removeKey" : action = "-"
  1732. Case Else : Fail "Unexpected Registry 'Action' value: " & value
  1733. End Select
  1734. If IsEmpty(action) Then
  1735. ProcessRegistry ElementText(node), root, key, name, data, component, op
  1736. For Each child In node.childNodes
  1737. Select Case (GetElementName(child))
  1738. Case Empty
  1739. Case "Permission" : ProcessPermissionElement child, ElementText(node), "Registry"
  1740. Case Else : Unexpected child, node
  1741. End Select
  1742. Next
  1743. Else
  1744. ProcessRegistryElement = Empty
  1745. Set row = installer.CreateRecord(UBound(RemoveRegistryTable))
  1746. row.StringData (RemoveRegistry_RemoveRegistry) = Modularize(ElementText(node)) '!!! auto-generate?
  1747. row.StringData (RemoveRegistry_Component_) = Modularize(component)
  1748. row.IntegerData(RemoveRegistry_Root) = root
  1749. row.StringData (RemoveRegistry_Key) = key
  1750. row.StringData (RemoveRegistry_Name) = action
  1751. DoAction RemoveRegistryTable, op, row
  1752. End If
  1753. End Function
  1754. Sub ProcessClassElement(node, component, feature)
  1755. Dim child, value, attribute, row, op
  1756. Dim bits, classId, context, defaultProgId, description, libId, threadingModel, insertable, version, programmable
  1757. Dim icon, iconIndex, server
  1758. Set row = installer.CreateRecord(UBound(ClassTable))
  1759. bits = 0
  1760. For Each attribute In node.Attributes
  1761. value = attribute.value
  1762. Select Case(attribute.name)
  1763. Case "op" : op = value
  1764. Case "Description" : description = value
  1765. Case "Context" : context = value
  1766. Case "AppId" : row.StringData (Class_AppId_) = "{" & value & "}"
  1767. Case "FileTypeMask" : row.StringData (Class_FileTypeMask) = value
  1768. Case "Icon" : icon = value
  1769. Case "IconIndex" : iconIndex = CInt(value)
  1770. Case "Handler" : row.StringData (Class_DefInprocHandler) = value
  1771. Case "Argument" : row.StringData (Class_Argument) = value
  1772. Case "RelativePath" : If value="yes" Then bits = bits Or msidbClassAttributesRelativePath
  1773. ' The following attributes result in rows added to the Registry table rather than the class table
  1774. Case "ThreadingModel" : threadingModel = value
  1775. Case "Version" : version = value
  1776. Case "Programmable" : If value="yes" Then programmable = "Programmable"
  1777. Case "Insertable"
  1778. Select Case(value)
  1779. Case "yes" : insertable = "Insertable"
  1780. Case "no" : insertable = "NotInsertable"
  1781. Case Else : Fail "Unexpected Class Insertable option: " & value
  1782. End Select
  1783. Case "Server" : server = value
  1784. Case Else : Unexpected attribute, node
  1785. End Select
  1786. Next
  1787. classId = "{" & ElementText(node) & "}"
  1788. For Each child In node.childNodes
  1789. Select Case (GetElementName(child))
  1790. Case Empty
  1791. Case "ProgId"
  1792. ProcessProgIdElement child, component, feature, classId, (description), Empty
  1793. If IsEmpty(defaultProgId) Then defaultProgId = ElementText(child)
  1794. Case "TypeLib"
  1795. libId = ProcessTypeLibElement(child, component, feature)
  1796. ProcessRegistry Empty, 0, "CLSID\" & classId & "\TypeLib",Empty,"{" & libId & "}",component,op
  1797. Case Else : Unexpected child, node
  1798. End Select
  1799. Next
  1800. If Not IsEmpty(threadingModel) Then
  1801. threadingModel = UCase(Left(threadingModel,1)) & Right(threadingModel, Len(threadingModel)-1)
  1802. ProcessRegistry Empty, 0, "CLSID\" & classId & "\" & context,"ThreadingModel",threadingModel,component,op
  1803. End If
  1804. If Not IsEmpty(version) Then ProcessRegistry Empty, 0, "CLSID\" & classId & "\Version", Empty, version,component,op
  1805. If Not IsEmpty(insertable) Then ProcessRegistry Empty, 0, "CLSID\" & classId & "\" & insertable, Empty, Empty,component,op
  1806. If Not IsEmpty(programmable) Then ProcessRegistry Empty, 0, "CLSID\" & classId & "\" & programmable, Empty, Empty,component,op
  1807. ' if this is being advertised under a feature
  1808. If Not IsEmpty(feature) Then
  1809. If Not IsEmpty(server) Then Fail "Cannot specify a Server for an advertised Class Id"
  1810. row.StringData (Class_CLSID) = classId
  1811. row.StringData (Class_Context) = context
  1812. row.StringData (Class_Component_) = Modularize(component)
  1813. row.StringData (Class_ProgId_Default) = defaultProgId
  1814. row.StringData (Class_Description) = description
  1815. row.StringData (Class_Icon_) = icon
  1816. row.StringData (Class_IconIndex) = iconIndex
  1817. row.StringData (Class_Feature_) = feature
  1818. If bits <> 0 Then row.IntegerData(Class_Attributes) = bits
  1819. DoAction ClassTable, op, row
  1820. Else
  1821. If IsEmpty(server) Then Fail "Must specify a Server for a non-advertised Class Id"
  1822. ' ClassId's Context
  1823. ProcessRegistry Empty, 0, "CLSID\" & classId & "\" & context,"", server, component, op
  1824. ' ClassId's Description
  1825. If 0 < Len(description) Then ProcessRegistry Empty, 0, "CLSID\" & classId, "", description, component, op
  1826. ' ClassId's AppId
  1827. If 0 < Len(row.StringData(Class_AppId_)) Then ProcessRegistry Empty, 0, "CLSID\" & classId & "\" & context,"AppID", row.StringData(Class_AppId_), component,op
  1828. ' ClassId's FileTypeMask
  1829. If 0 < Len(row.StringData(Class_FileTypeMask)) Then Fail "Don't know how to convert FileTypeMask into Registry elements - robmen"
  1830. ' ClassId's Default Icon
  1831. If 0 < Len(icon) Then
  1832. If 0 < Len(iconIndex) Then icon = icon & "," & iconIndex
  1833. ProcessRegistry Empty, 0, "CLSID\" & classId & "\" & context & "\DefaultIcon","", icon, component,op
  1834. End If
  1835. ' ClassId's Handler
  1836. If 0 < Len(row.StringData(Class_DefInprocHandler)) Then
  1837. Select Case row.StringData(Class_DefInprocHandler)
  1838. Case "1" : ProcessRegistry Empty, 0, "CLSID\" & classId & "\InprocHandler","", "ole.dll", component,op
  1839. Case "2" : ProcessRegistry Empty, 0, "CLSID\" & classId & "\InprocHandler32","", "ole32.dll", component,op
  1840. Case "3"
  1841. ProcessRegistry Empty, 0, "CLSID\" & classId & "\InprocHandler","", "ole.dll", component,op
  1842. ProcessRegistry Empty, 0, "CLSID\" & classId & "\InprocHandler32","", "ole32.dll", component,op
  1843. Case Else : ProcessRegistry regId, 0, "CLSID\" & classId & "\InprocHandler32","", row.StringData(Class_DefInprocHandler), component,op
  1844. End Select
  1845. End If
  1846. ' ClassId's Argument
  1847. If 0 < Len(row.StringData(Class_Argument)) Then Fail "Don't know how to convert Arguments into Registry elements - robmen"
  1848. ' ClassId's RelativePath
  1849. If 0 < Len(row.StringData(Class_Argument)) Then Fail "Don't know how to convert RelativePath into Registry elements - robmen"
  1850. End If
  1851. End Sub
  1852. Sub ProcessProgIdElement(node, component, feature, classId, description, parent)
  1853. Dim child, value, attribute, row, op, progId, icon, iconIndex
  1854. Set row = installer.CreateRecord(UBound(ProgIdTable))
  1855. progId = ElementText(node)
  1856. For Each attribute In node.Attributes
  1857. value = attribute.value
  1858. Select Case(attribute.name)
  1859. Case "op" : op = value
  1860. Case "Description" : description = value
  1861. Case "Icon" : icon = Modularize(value)
  1862. Case "IconIndex" : iconIndex = CInt(value)
  1863. Case Else : Unexpected attribute, node
  1864. End Select
  1865. Next
  1866. For Each child In node.childNodes
  1867. Select Case (GetElementName(child))
  1868. Case Empty
  1869. Case "Extension" : ProcessExtensionElement child, component, feature, progId
  1870. Case "ProgId"
  1871. If IsEmpty(feature) Then
  1872. ProcessProgIdElement child, component, Empty, (classId), (description), progId
  1873. Else
  1874. ProcessProgIdElement child, component, feature, Empty, (description), progId
  1875. End If
  1876. Case Else : Unexpected child, node
  1877. End Select
  1878. Next
  1879. If IsEmpty(feature) Then
  1880. ' ProgId
  1881. ProcessRegistry Empty, 0, progId, "", description, component, op
  1882. ' ProgId's ClassId
  1883. If 0 < Len(classId) Then
  1884. ProcessRegistry Empty, 0, progId & "\CLSID", "", classId, component, op
  1885. ' if this is a version independent ProgId
  1886. If 0 < Len(parent) Then
  1887. ProcessRegistry Empty, 0, "CLSID\" & classId & "\VersionIndependentProgID", "", progId, component, op
  1888. Else
  1889. ProcessRegistry Empty, 0, "CLSID\" & classId & "\ProgID", "", progId, component, op
  1890. End If
  1891. End If
  1892. ' ProgId's Default Icon
  1893. If 0 < Len(icon) Then
  1894. If 0 < Len(iconIndex) Then icon = icon & "," & iconIndex
  1895. ProcessRegistry Empty, 0, progId & "\DefaultIcon","", icon, component,op
  1896. End If
  1897. Else
  1898. row.StringData (ProgId_ProgId) = progId
  1899. row.StringData (ProgId_ProgId_Parent) = parent
  1900. row.StringData (ProgId_Class_) = classId
  1901. row.StringData (ProgId_Description) = description
  1902. row.StringData (ProgId_Icon_) = icon
  1903. If Not IsEmpty(iconIndex) Then row.IntegerData(ProgId_IconIndex) = iconIndex
  1904. DoAction ProgIdTable, op, row
  1905. End If
  1906. End Sub
  1907. Sub ProcessExtensionElement(node, component, feature, progId)
  1908. Dim child, value, attribute, row, op, extension, mime
  1909. Set row = installer.CreateRecord(UBound(ExtensionTable))
  1910. extension = ElementText(node)
  1911. For Each attribute In node.Attributes
  1912. value = attribute.value
  1913. Select Case(attribute.name)
  1914. Case "op" : op = value
  1915. Case "ContentType" : mime = value
  1916. Case Else : Unexpected attribute, node
  1917. End Select
  1918. Next
  1919. For Each child In node.childNodes
  1920. Select Case (GetElementName(child))
  1921. Case Empty
  1922. Case "Verb" : ProcessVerbElement child, extension, progId, feature
  1923. Case "MIME"
  1924. value = ProcessMIMEElement(child, extension, feature)
  1925. If value <> "" And IsEmpty(mime) Then mime = value
  1926. Case Else : Unexpected child, node
  1927. End Select
  1928. Next
  1929. If IsEmpty(feature) Then
  1930. ' Extension
  1931. ProcessRegistry Empty, 0, "." & extension, "", progId, component, op
  1932. ' Extension's MIME ContentType
  1933. If 0 < Len(mime) Then ProcessRegistry Empty, 0, "." & extension, "Content Type", mime, component, op
  1934. Else
  1935. row.StringData (Extension_Extension) = extension
  1936. row.StringData (Extension_Component_) = Modularize(component)
  1937. row.StringData (Extension_ProgId_) = progId
  1938. row.StringData (Extension_MIME_) = mime
  1939. row.StringData (Extension_Feature_) = feature
  1940. DoAction ExtensionTable, op, row
  1941. End If
  1942. End Sub
  1943. Sub ProcessVerbElement(node, extension, progId, feature)
  1944. Dim attribute, value, row, op, target, command, argument, sequence
  1945. Set row = installer.CreateRecord(UBound(VerbTable))
  1946. For Each attribute In node.Attributes
  1947. value = attribute.value
  1948. Select Case(attribute.name)
  1949. Case "op" : op = value
  1950. Case "Target" : target = value
  1951. Case "Command" : command = value
  1952. Case "Argument" : argument = value
  1953. case "Sequence" : sequence = CLng(value)
  1954. Case Else : Unexpected attribute, node
  1955. End Select
  1956. Next
  1957. If IsEmpty(feature) Then
  1958. If Not IsEmpty(target) Then Fail "Must specify a Target for a non-advertised Verb"
  1959. ' handle arguments
  1960. If 0 < Len(argument) Then target = target & " " & argument
  1961. ' handle if verb is under progId or under extension
  1962. If 0 < Len(progId) Then
  1963. ProcessRegistry Empty, 0, progId & "\shell\" & command & "\command", "", target, component, op
  1964. Else
  1965. ProcessRegistry Empty, 0, "." & extension & "\shell\" & command & "\command", "", target, component, op
  1966. End If
  1967. Else
  1968. If Not IsEmpty(target) Then Fail "Cannot specify a Target for an advertised Verb"
  1969. row.StringData (Verb_Extension_) = extension
  1970. row.StringData (Verb_Verb) = ElementText(node)
  1971. If Not IsEmpty(sequence) Then row.IntegerData(Verb_Sequence) = sequence
  1972. row.StringData (Verb_Command) = command
  1973. row.StringData (Verb_Argument) = argument
  1974. DoAction VerbTable, op, row
  1975. End If
  1976. End Sub
  1977. Function ProcessMIMEElement(node, extension, feature)
  1978. Dim attribute, value, row, op, contentType, classId
  1979. Set row = installer.CreateRecord(UBound(MIMETable))
  1980. contentType = ElementText(node)
  1981. For Each attribute In node.Attributes
  1982. value = attribute.value
  1983. Select Case(attribute.name)
  1984. Case "op" : op = value
  1985. Case "Class" : classId = value
  1986. Case "Default" : If value = "yes" Then ProcessMIMEElement = contentType
  1987. Case Else : Unexpected attribute, node
  1988. End Select
  1989. Next
  1990. If IsEmpty(feature) Then
  1991. ProcessRegistry Empty, 0, "MIME\Content Type\" & contentType & "", "Extension", extension, component, op
  1992. If 0 < Len(classId) Then ProcessRegistry Empty, 0, "MIME\Content Type\" & contentType & "", "CLSID", classId, component, op
  1993. Else
  1994. row.StringData (MIME_ContentType) = contentType
  1995. row.StringData (MIME_Extension_) = extension
  1996. row.StringData (MIME_CLSID) = classId
  1997. DoAction MIMETable, op, row
  1998. End If
  1999. End Function
  2000. Function ProcessTypeLibElement(node, component, feature)
  2001. Dim value, attribute, row, op, version
  2002. Set row = installer.CreateRecord(UBound(TypeLibTable))
  2003. version = 0
  2004. For Each attribute In node.Attributes
  2005. value = attribute.value
  2006. Select Case(attribute.name)
  2007. Case "op" : op = value
  2008. Case "MajorVersion" : version = CInt(value) * 256 + version
  2009. Case "MinorVersion" : version = CInt(value) + version
  2010. Case "Language" : row.IntegerData(TypeLib_Language) = CInt(value)
  2011. Case "HelpDirectory" : row.StringData (TypeLib_Directory_) = Modularize(value)
  2012. Case "Description" : row.StringData (TypeLib_Description) = value
  2013. Case "dt" ' bug in IE5 msxml
  2014. Case Else : Unexpected attribute, node
  2015. End Select
  2016. Next
  2017. If fModule Then feature = "{00000000-0000-0000-0000-000000000000}"
  2018. ProcessTypeLibElement = ElementText(node)
  2019. row.StringData (TypeLib_LibID) = "{" & ProcessTypeLibElement & "}"
  2020. row.StringData (TypeLib_Component_) = Modularize(component)
  2021. row.StringData (TypeLib_Feature_) = feature
  2022. row.IntegerData(TypeLib_Version) = version
  2023. DoAction TypeLibTable, op, row
  2024. End Function
  2025. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  2026. ' ProcessMediaElement
  2027. Sub ProcessMediaElement(node, lastId)
  2028. Dim value, attribute, child, row, op, diskId
  2029. Dim nLastSequence, sCabinet, fEmbed
  2030. nLastSequence = 0
  2031. fEmbed = False
  2032. Set row = installer.CreateRecord(UBound(MediaTable))
  2033. For Each attribute In node.Attributes
  2034. value = attribute.value
  2035. Select Case(attribute.name)
  2036. Case "op" : op = value
  2037. Case "DiskId" : diskId = value
  2038. Case "LastSequence" : nLastSequence = CInt(value)
  2039. Case "DiskPrompt" : row.StringData (Media_DiskPrompt) = value
  2040. Case "Cabinet" : sCabinet = value
  2041. Case "EmbedCab" : If "yes" = value Then fEmbed = True
  2042. Case "VolumeLabel" : row.StringData (Media_VolumeLabel) = value
  2043. Case Else : Unexpected attribute, node
  2044. End Select
  2045. Next
  2046. If IsEmpty(diskId) Then diskId = lastId + 1
  2047. lastId = diskId
  2048. If fEmbed Then
  2049. If 0 = Len(sCabinet) Then Fail "Must specify a 'Cabinet' when embedding"
  2050. If "#" <> Left(sCabinet, 1) Then sCabinet = "#" & sCabinet
  2051. End If
  2052. row.IntegerData(Media_DiskId) = CInt(diskId)
  2053. row.IntegerData(Media_LastSequence) = nLastSequence
  2054. row.StringData (Media_Cabinet) = sCabinet
  2055. DoAction MediaTable, op, row
  2056. For Each child In node.childNodes
  2057. Select Case (GetElementName(child))
  2058. Case Empty
  2059. Case "DigitalSignature" : ProcessDigitalSignatureElement child, "Media", diskId
  2060. Case Else : Unexpected child, node
  2061. End Select
  2062. Next
  2063. End Sub ' ProcessMediaElement
  2064. Sub ProcessAppIdElement(node)
  2065. Dim value, attribute, row, op
  2066. Set row = installer.CreateRecord(UBound(AppIdTable))
  2067. version = 0
  2068. For Each attribute In node.Attributes
  2069. value = attribute.value
  2070. Select Case(attribute.name)
  2071. Case "op" : op = value
  2072. Case "RemoteServerName" : row.StringData(AppId_RemoteServerName) = value
  2073. Case "LocalService" : row.StringData(AppId_LocalService) = value
  2074. Case "ServiceParameters" : row.StringData(AppId_ServiceParameters) = value
  2075. Case "DllSurrogate" : row.StringData(AppId_DllSurrogate) = value
  2076. Case "ActivateAtStorage" : If value = "yes" Then row.IntegerData(AppId_ActivateAtStorage) = 1
  2077. Case "RunAsInteractiveUser" : If value = "yes" Then row.IntegerData(AppId_RunAsInteractiveUser) = 1
  2078. Case Else : Unexpected attribute, node
  2079. End Select
  2080. Next
  2081. row.StringData (AppId_AppId) = "{" & ElementText(node) & "}"
  2082. DoAction AppIdTable, op, row
  2083. End Sub
  2084. Sub ProcessCustomActionElement(node)
  2085. Dim value, attribute, row, op, source, target, bits, sourceBits, targetBits
  2086. Set row = installer.CreateRecord(UBound(CustomActionTable))
  2087. bits = 0
  2088. For Each attribute In node.Attributes
  2089. value = attribute.value
  2090. Select Case(attribute.name)
  2091. Case "op" : op = value
  2092. Case "BinaryKey" : source = value : sourceBits = msidbCustomActionTypeBinaryData
  2093. Case "FileKey" : source = value : sourceBits = msidbCustomActionTypeSourceFile
  2094. Case "Property" : source = value : sourceBits = msidbCustomActionTypeProperty
  2095. Case "Directory" : source = value : sourceBits = msidbCustomActionTypeDirectory
  2096. Case "DllEntry" : target = value : targetBits = msidbCustomActionTypeDll
  2097. Case "ExeCommand" : target = value : targetBits = msidbCustomActionTypeExe
  2098. Case "JScriptCall" : target = value : targetBits = msidbCustomActionTypeJScript
  2099. Case "VBScriptCall" : target = value : targetBits = msidbCustomActionTypeVBScript
  2100. Case "Value" : target = value : targetBits = msidbCustomActionTypeTextData
  2101. Case "InstallProperties" : target = value : targetBits = msidbCustomActionTypeInstall
  2102. Case "Impersonate" : If value="no" Then bits = bits Or msidbCustomActionTypeNoImpersonate
  2103. Case "Return"
  2104. Select Case(value)
  2105. Case "check"
  2106. Case "ignore" : bits = bits Or msidbCustomActionTypeContinue
  2107. Case "asyncWait" : bits = bits Or msidbCustomActionTypeAsync
  2108. Case "asyncNoWait" : bits = bits Or msidbCustomActionTypeAsync Or msidbCustomActionTypeContinue
  2109. Case Else : Fail "Unknown CustomAction Return type: " & value
  2110. End Select
  2111. Case "Execute"
  2112. Select Case(value)
  2113. Case "immediate"
  2114. Case "deferred" : bits = bits Or msidbCustomActionTypeInScript
  2115. Case "rollback" : bits = bits Or msidbCustomActionTypeInScript Or msidbCustomActionTypeRollback
  2116. Case "commit" : bits = bits Or msidbCustomActionTypeInScript Or msidbCustomActionTypeCommit
  2117. Case "oncePerProcess" : bits = bits Or msidbCustomActionTypeOncePerProcess
  2118. Case "firstSequence" : bits = bits Or msidbCustomActionTypeFirstSequence
  2119. Case "secondSequence" : bits = bits Or msidbCustomActionTypeClientRepeat
  2120. Case Else : Fail "Unknown CustomAction Execute type: " & value
  2121. End Select
  2122. Case Else : Unexpected attribute, node
  2123. End Select
  2124. Next
  2125. row.StringData (CustomAction_Action) = Modularize(ElementText(node))
  2126. row.IntegerData(CustomAction_Type) = bits Or sourceBits Or targetBits
  2127. row.StringData (CustomAction_Source) = Modularize(source)
  2128. row.StringData (CustomAction_Target) = target
  2129. DoAction CustomActionTable, op, row
  2130. End Sub
  2131. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  2132. ' ProcessCustomTableElement
  2133. Sub ProcessCustomTableElement(node)
  2134. Dim tableName, columnName, columnCount, rowCount, customTable(), columnNames(32), columnTypes(32), columnDef
  2135. Dim value, attribute, child, item, state, row, op, primaryKey, width, nullable, localizable, typeName, index
  2136. For Each attribute In node.Attributes
  2137. Select Case(attribute.name)
  2138. Case "op" : op = attribute.value
  2139. Case Else : Unexpected attribute, node
  2140. End Select
  2141. Next
  2142. tableName = ElementText(node)
  2143. ReDim customTable(32)
  2144. customTable(0) = tableName
  2145. columnCount = 0
  2146. For Each child In node.childNodes
  2147. Next
  2148. For Each child In node.childNodes
  2149. Select Case (GetElementName(child))
  2150. Case Empty
  2151. Case "Column"
  2152. columnCount = columnCount + 1
  2153. columnName = ElementText(child)
  2154. columnNames(columnCount) = columnName
  2155. primaryKey = False
  2156. nullable = False
  2157. localizable = False
  2158. width = 0
  2159. For Each attribute In child.Attributes
  2160. value = attribute.value
  2161. Select Case(attribute.name)
  2162. Case "op" : op = value
  2163. Case "PrimaryKey" : If value = "yes" Then primaryKey = True
  2164. Case "Nullable" : If value = "yes" Then nullable = True
  2165. Case "Localizable": If value = "yes" Then localizable= True
  2166. Case "Width" : width = CInt(value)
  2167. Case "Type"
  2168. Select Case(value)
  2169. Case "int" : typeName = "SHORT"
  2170. Case "string" : typeName = "CHAR"
  2171. Case "binary" : typeName = "OBJECT"
  2172. Case Else : Fail "Unknown CustomTable data type: " & value
  2173. End Select
  2174. columnTypes(columnCount) = value
  2175. Case Else : Unexpected attribute, node
  2176. End Select
  2177. Next
  2178. If typeName = "SHORT" Then
  2179. If width = 4 Then typeName = "LONG" Else If width <> 2 Then Fail "Invalid integer width: " & width
  2180. End If
  2181. If typeName = "CHAR" Then
  2182. If width = 0 Then typeName = "LONGCHAR" Else typeName = typeName & "(" & width & ")"
  2183. End If
  2184. columnDef = "`" & columnName & "` " & typeName
  2185. If Not nullable Then columnDef = columnDef & " NOT NULL"
  2186. If primaryKey Then columnDef = columnDef & " PRIMARY KEY"
  2187. If localizable Then columnDef = columnDef & " LOCALIZABLE"
  2188. customTable(columnCount) = columnDef
  2189. Case "Row" ' processed on second pass
  2190. Case Else : Unexpected child, node
  2191. End Select
  2192. Next
  2193. ReDim Preserve customTable(columnCount)
  2194. CreateView(customTable) 'CreateView will call CreateTable if table doesn't already exist
  2195. Set row = installer.CreateRecord(columnCount)
  2196. For Each child In node.childNodes
  2197. Select Case (GetElementName(child))
  2198. Case Empty
  2199. Case "Column" ' columns already processed
  2200. Case "Row"
  2201. row.ClearData
  2202. For Each attribute In child.Attributes
  2203. Select Case(attribute.name)
  2204. Case "op" : op = attribute.value
  2205. Case Else : Unexpected attribute, node
  2206. End Select
  2207. Next
  2208. For Each item In child.childNodes
  2209. Select Case (GetElementName(item))
  2210. Case Empty
  2211. Case "Data"
  2212. columnName = Empty
  2213. For Each attribute In item.Attributes
  2214. Select Case(attribute.name)
  2215. Case "op" : op = attribute.value
  2216. Case "Column" : columnName = attribute.value
  2217. Case Else : Unexpected attribute, node
  2218. End Select
  2219. Next
  2220. If IsEmpty(columnName) Then Fail "Missing column name for Data"
  2221. For index = columnCount To 0 Step -1
  2222. If columnNames(index) = columnName Then Exit For
  2223. Next
  2224. If index = 0 Then Fail "Undefined column for Data: " & columnName
  2225. value = ElementText(item)
  2226. If columnTypes(index) = "string" Then
  2227. row.StringData(index) = value
  2228. Else
  2229. row.IntegerData(index) = CLng(value)
  2230. End If
  2231. Case Else : Unexpected child, node
  2232. End Select
  2233. Next
  2234. DoAction customTable, op, row
  2235. Case Else : Unexpected child, node
  2236. End Select
  2237. Next
  2238. End Sub ' ProcessCustomTableElement
  2239. Sub ProcessBinaryElement(node, table)
  2240. Dim attribute, op, child, value, row, name, length, fileName, outPath, binStream, line, index, char, nxtc, state
  2241. If fNoBinary Then Exit Sub
  2242. Set row = installer.CreateRecord(UBound(table))
  2243. For Each attribute In node.Attributes
  2244. value = attribute.value
  2245. Select Case(attribute.name)
  2246. Case "op" : op = value
  2247. Case "Name" : name = value
  2248. Case "src" : outPath = BaseDir(value)
  2249. Case Else : Unexpected attribute, node
  2250. End Select
  2251. Next
  2252. ' if a file wasn't specified process the bin encoded data
  2253. If IsEmpty(outPath) Then
  2254. fileName = name
  2255. outPath = tempDir & "\wi.tmp"
  2256. Set binStream = fso.CreateTextFile(outPath, OverwriteIfExist, OpenAsASCII) : CheckError
  2257. If fVerbose Then Wscript.echo "Binary file: " & name & " --> " & outPath
  2258. For Each child In node.childNodes
  2259. If child.nodeType = NODE_TEXT Then
  2260. value = child.text
  2261. length = Len(value)
  2262. line = Empty
  2263. For index = 1 To length
  2264. char = Asc(Mid(value, index, 1))
  2265. If char < 48 Then '0
  2266. If char = 47 Then '/
  2267. char = 63
  2268. ElseIf char = 43 Then '+
  2269. char = 62
  2270. Else
  2271. Fail "Illegal bin.base64 char: " & char
  2272. End If
  2273. ElseIf char = 61 Then '=
  2274. state = 4
  2275. ElseIf char <= 57 Then
  2276. char = char + 4 '9
  2277. ElseIf char <= 90 Then
  2278. char = char - 65 'Z
  2279. ElseIf char <= 122 Then
  2280. char = char - 71 'z
  2281. Else
  2282. Fail "Illegal bin.base64 char: " & char
  2283. End If
  2284. Select Case (state)
  2285. Case 0: state = 1 : nxtc = char * 4
  2286. Case 1: state = 2 : line = line & Chr((char \ 16) + nxtc) : nxtc = (char Mod 16) * 16
  2287. Case 2: state = 3 : line = line & Chr((char \ 4) + nxtc) : nxtc = (char Mod 4) * 64
  2288. Case 3: state = 0 : line = line & Chr( char + nxtc)
  2289. Case Else: state = 0
  2290. End Select
  2291. Next
  2292. binStream.Write line
  2293. End If
  2294. Next
  2295. binStream.Close
  2296. Set binStream = Nothing ' release to allow reading
  2297. End If
  2298. ' On Error Resume Next
  2299. row.StringData(Binary_Name) = Modularize(name)
  2300. row.SetStream Binary_Data, outPath : CheckError
  2301. DoAction table, op, row
  2302. CloseView table ' force table out of memory to release file
  2303. End Sub
  2304. Sub ProcessSequence(table, node)
  2305. Dim child, attribute, row, op, action, sequence, condition, lastSequence, defaultOp
  2306. If fNoSeqTables Then Exit Sub
  2307. Set row = installer.CreateRecord(UBound(table))
  2308. For Each attribute In node.Attributes
  2309. Select Case(attribute.name)
  2310. Case "op" : defaultOp = attribute.value
  2311. Case "xmlns" ' ignore, processed by XML engine
  2312. Case Else : Unexpected attribute, node
  2313. End Select
  2314. Next
  2315. ' due to a bug in the mergemod.dll add all sequences to a MSI when ever the first one is needed
  2316. If database.TablePersistent("InstallExecuteSequence") = 2 Then CreateTable InstallExecuteSequenceTable
  2317. If database.TablePersistent("InstallUISequence") = 2 Then CreateTable InstallUISequenceTable
  2318. If database.TablePersistent("AdminExecuteSequence") = 2 Then CreateTable AdminExecuteSequenceTable
  2319. If database.TablePersistent("AdminUISequence") = 2 Then CreateTable AdminUISequenceTable
  2320. If database.TablePersistent("AdvtExecuteSequence") = 2 Then CreateTable AdvtExecuteSequenceTable
  2321. If database.TablePersistent("AdvtUISequence") = 2 Then CreateTable AdvtUISequenceTable
  2322. For Each child In node.childNodes
  2323. action = GetElementName(child)
  2324. If Not IsEmpty(action) Then
  2325. sequence = Empty
  2326. condition = Empty
  2327. op = Empty
  2328. For Each attribute In child.Attributes
  2329. Select Case(attribute.name)
  2330. Case "op" : op = attribute.value
  2331. Case "xmlns" ' ignore, processed by XML engine
  2332. Case "Action" : If action = "Custom" Then action = attribute.value Else Unexpected attribute, child
  2333. Case "Dialog" : If action = "Show" Then action = attribute.value Else Unexpected attribute, child
  2334. Case "Sequence" : sequence = CInt(attribute.value)
  2335. Case "OnExit"
  2336. If Not IsEmpty(sequence) Then Fail "Can't specify both Sequence and OnExit"
  2337. Select Case(attribute.value)
  2338. Case "success" : sequence = -1
  2339. Case "cancel" : sequence = -2
  2340. Case "error" : sequence = -3
  2341. Case "suspend" : sequence = -4
  2342. Case Else : Fail "Unexpected OnExit value: " & attribute.value
  2343. End Select
  2344. Case Else : Unexpected attribute, child
  2345. End Select
  2346. Next
  2347. If action = "Custom" Then Fail "Missing Action attribute for Custom action"
  2348. If action = "Show" Then Fail "Missing Dialog attribute for Show action"
  2349. If ElementHasText(child) Then condition = ElementText(child)
  2350. If IsEmpty(sequence) Or sequence = 0 Then sequence = lastSequence + 1
  2351. If IsEmpty(op) Then op = defaultOp
  2352. lastSequence = sequence
  2353. row.StringData (InstallExecuteSequence_Action) = action
  2354. row.IntegerData(InstallExecuteSequence_Sequence) = sequence
  2355. row.StringData (InstallExecuteSequence_Condition) = condition
  2356. DoAction table, op, row
  2357. End If
  2358. Next
  2359. End Sub
  2360. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  2361. ' ODBC handlers
  2362. Sub ProcessODBCDriver(node, component, file, table) ' also handles ODBCTranslator
  2363. Dim child, value, attribute, row, op, driver, setup, name, driverKey, childOp
  2364. Set row = installer.CreateRecord(UBound(table))
  2365. driver = file
  2366. setup = file
  2367. driverKey = ElementText(node)
  2368. For Each attribute In node.Attributes
  2369. value = attribute.value
  2370. Select Case(attribute.name)
  2371. Case "op" : op = value
  2372. Case "Name" : name = value
  2373. Case "File" : driver = value
  2374. Case "SetupFile" : setup = value
  2375. Case Else : Unexpected attribute, node
  2376. End Select
  2377. Next
  2378. row.StringData(ODBCDriver_Driver) = driverKey
  2379. row.StringData(ODBCDriver_Component_) = Modularize(component)
  2380. row.StringData(ODBCDriver_Description) = name
  2381. row.StringData(ODBCDriver_File_) = Modularize(driver)
  2382. row.StringData(ODBCDriver_File_Setup) = Modularize(setup)
  2383. DoAction table, op, row
  2384. If table(0) <> "`ODBCDriver`" Then Exit Sub ' translators have no attributes or data sources
  2385. For Each child In node.childNodes
  2386. childOp = op
  2387. Select Case (GetElementName(child))
  2388. Case Empty
  2389. Case "ODBCDataSource" : Call ProcessODBCDataSource(child, component, name)
  2390. Case "Property"
  2391. row.ClearData
  2392. For Each attribute In child.Attributes
  2393. Select Case(attribute.name)
  2394. Case "op" : childOp = attribute.value
  2395. Case "Value" : value = attribute.value
  2396. Case Else : Unexpected attribute, child
  2397. End Select
  2398. Next
  2399. row.StringData(ODBCAttribute_Driver_) = driverKey
  2400. row.StringData(ODBCAttribute_Attribute) = ElementText(child)
  2401. row.StringData(ODBCAttribute_Value) = value
  2402. DoAction ODBCAttributeTable, childOp, row
  2403. Case Else : Unexpected child, node
  2404. End Select
  2405. Next
  2406. End Sub
  2407. Function ProcessODBCDataSource(node, component, driverName)
  2408. Dim child, value, attribute, row, op, name, sourceKey, childOp, registration
  2409. Set row = installer.CreateRecord(UBound(ODBCDataSourceTable))
  2410. sourceKey = ElementText(node)
  2411. ProcessODBCDataSource = "reg"
  2412. For Each attribute In node.Attributes
  2413. value = attribute.value
  2414. Select Case(attribute.name)
  2415. Case "op" : op = value
  2416. Case "Name" : name = value
  2417. Case "DriverName" : driverName = value
  2418. Case "KeyPath" : ProcessODBCDataSource = value
  2419. Case "Registration"
  2420. Select Case(value)
  2421. Case "machine" : registration = 0
  2422. Case "user" : registration = 1
  2423. Case Else : Fail "Unexpected ODBCDataSource Registration: " & value
  2424. End Select
  2425. Case Else : Unexpected attribute, node
  2426. End Select
  2427. Next
  2428. row.StringData (ODBCDataSource_DataSource) = sourceKey
  2429. row.StringData (ODBCDataSource_Component_) = Modularize(component)
  2430. row.StringData (ODBCDataSource_Description) = name
  2431. row.StringData (ODBCDataSource_DriverDescription) = driverName
  2432. row.IntegerData(ODBCDataSource_Registration) = regitration
  2433. DoAction ODBCDataSourceTable, op, row
  2434. For Each child In node.childNodes
  2435. childOp = op
  2436. Select Case (GetElementName(child))
  2437. Case Empty
  2438. Case "Property"
  2439. row.ClearData
  2440. For Each attribute In child.Attributes
  2441. Select Case(attribute.name)
  2442. Case "op" : childOp = attribute.value
  2443. Case "Value" : value = attribute.value
  2444. Case Else : Unexpected attribute, child
  2445. End Select
  2446. Next
  2447. row.StringData(ODBCSourceAttribute_DataSource_) = sourceKey
  2448. row.StringData(ODBCSourceAttribute_Attribute) = ElementText(child)
  2449. row.StringData(ODBCSourceAttribute_Value) = value
  2450. DoAction ODBCSourceAttributeTable, childOp, row
  2451. Case Else : Unexpected child, node
  2452. End Select
  2453. Next
  2454. End Function
  2455. '---------------------------------------------------------------------------------'
  2456. ' AppSearch and CCP handlers
  2457. '---------------------------------------------------------------------------------'
  2458. Sub ProcessAppSearch(property, signature, op)
  2459. If property <> UCase(property) Then Fail "Must uppercase search Property: " & property
  2460. Dim row : Set row = installer.CreateRecord(UBound(AppSearchTable))
  2461. row.StringData (AppSearch_Property) = Modularize(property)
  2462. row.StringData (AppSearch_Signature_) = Modularize(signature)
  2463. DoAction AppSearchTable, op, row
  2464. End Sub
  2465. Function ProcessIniFileSearchElement(node)
  2466. Dim child, op, row, attribute, value, signature, fOneChild
  2467. Set row = installer.CreateRecord(UBound(IniLocatorTable))
  2468. For Each attribute In node.Attributes
  2469. value = attribute.value
  2470. Select Case(attribute.name)
  2471. Case "op" : op = value
  2472. Case "File" : row.StringData (IniLocator_FileName) = value
  2473. Case "Section" : row.StringData (IniLocator_Section) = value
  2474. Case "Key" : row.StringData (IniLocator_Key) = value
  2475. Case "Field" : row.StringData (IniLocator_Field) = value
  2476. Case "Type"
  2477. Select Case(value)
  2478. Case "directory" : row.IntegerData(IniLocator_Type) = 0
  2479. Case "file" : row.IntegerData(IniLocator_Type) = 1
  2480. Case "registry" : row.IntegerData(IniLocator_Type) = 2
  2481. Case Else : Fail "Unknown Ini search type: " & value
  2482. End Select
  2483. End Select
  2484. Next
  2485. signature = Modularize(ElementText(node)) ' !! maybe auto-generate?
  2486. row.StringData (IniLocator_Signature_) = Modularize(signature)
  2487. DoAction IniLocatorTable, op, row
  2488. fOneChild = False
  2489. For Each child In node.childNodes
  2490. Select Case (GetElementName(child))
  2491. Case Empty
  2492. Case "DirectorySearch": If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessDirectorySearchElement(child, signature)
  2493. Case "FileSearch" : If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessFileSearchElement (child, signature)
  2494. Case Else : Unexpected child, node
  2495. End Select
  2496. Next
  2497. ProcessIniFileSearchElement = signature
  2498. End Function ' ProcessIniFileSearchElement
  2499. Function ProcessRegistrySearchElement(node)
  2500. Dim child, op, row, attribute, value, signature, fOneChild
  2501. Set row = installer.CreateRecord(UBound(RegLocatorTable))
  2502. For Each attribute In node.Attributes
  2503. value = attribute.value
  2504. Select Case(attribute.name)
  2505. Case "op" : op = value
  2506. Case "Root"
  2507. Select Case(value)
  2508. Case "HKCR" : row.IntegerData(RegLocator_Root) = 0
  2509. Case "HKCU" : row.IntegerData(RegLocator_Root) = 1
  2510. Case "HKLM" : row.IntegerData(RegLocator_Root) = 2
  2511. Case "HKU" : row.IntegerData(RegLocator_Root) = 3
  2512. Case Else : Fail "Unknown Registry search type: " & value
  2513. End Select
  2514. Case "Key" : row.StringData (RegLocator_Key) = value
  2515. Case "Name" : row.StringData (RegLocator_Name) = value
  2516. Case "Type"
  2517. Select Case(value)
  2518. Case "directory" : row.IntegerData(RegLocator_Type) = 0
  2519. Case "file" : row.IntegerData(RegLocator_Type) = 1
  2520. Case "registry" : row.IntegerData(RegLocator_Type) = 2
  2521. Case Else : Fail "Unknown Registry search type: " & value
  2522. End Select
  2523. End Select
  2524. Next
  2525. signature = ElementText(node) ' !! maybe auto-generate?
  2526. row.StringData (RegLocator_Signature_) = Modularize(signature)
  2527. DoAction RegLocatorTable, op, row
  2528. fOneChild = False
  2529. For Each child In node.childNodes
  2530. Select Case (GetElementName(child))
  2531. Case Empty
  2532. Case "DirectorySearch": If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessDirectorySearchElement(child, signature)
  2533. Case "FileSearch" : If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessFileSearchElement (child, signature)
  2534. Case Else : Unexpected child, node
  2535. End Select
  2536. Next
  2537. ProcessRegistrySearchElement = signature
  2538. End Function ' ProcessRegistrySearchElement
  2539. Function ProcessComponentSearchElement(node)
  2540. Dim child, op, row, attribute, value, signature, fOneChild
  2541. Set row = installer.CreateRecord(UBound(CompLocatorTable))
  2542. For Each attribute In node.Attributes
  2543. value = attribute.value
  2544. Select Case(attribute.name)
  2545. Case "op" : op = value
  2546. Case "Id" : row.StringData (CompLocator_ComponentId) = "{" & value & "}" ' add curly braces on GUID
  2547. Case "Type"
  2548. Select Case(value)
  2549. Case "directory" : row.IntegerData(CompLocator_Type) = 0
  2550. Case "file" : row.IntegerData(CompLocator_Type) = 1
  2551. Case Else : Fail "Unknown Component search type: " & value
  2552. End Select
  2553. End Select
  2554. Next
  2555. signature = ElementText(node) ' !! maybe auto-generate?
  2556. row.StringData (CompLocator_Signature_) = Modularize(signature)
  2557. DoAction CompLocatorTable, op, row
  2558. fOneChild = False
  2559. For Each child In node.childNodes
  2560. Select Case (GetElementName(child))
  2561. Case Empty
  2562. Case "DirectorySearch": If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessDirectorySearchElement(child, signature)
  2563. Case "FileSearch" : If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessFileSearchElement (child, signature)
  2564. Case Else : Unexpected child, node
  2565. End Select
  2566. Next
  2567. ProcessComponentSearchElement = signature
  2568. End Function ' ProcessComponentSearchElement
  2569. Function ProcessDirectorySearchElement(node, parent)
  2570. Dim child, op, row, attribute, value, signature, fOneChild
  2571. Set row = installer.CreateRecord(UBound(DrLocatorTable))
  2572. For Each attribute In node.Attributes
  2573. value = attribute.value
  2574. Select Case(attribute.name)
  2575. Case "op" : op = value
  2576. Case "Path" : row.StringData (DrLocator_Path) = value
  2577. Case "Depth" : row.IntegerData(DrLocator_Depth) = CInt(value)
  2578. End Select
  2579. Next
  2580. signature = ElementText(node) ' !! maybe auto-generate?
  2581. row.StringData (DrLocator_Signature_) = Modularize(signature)
  2582. row.StringData (DrLocator_Parent) = Modularize(parent)
  2583. DoAction DrLocatorTable, op, row
  2584. fOneChild = False
  2585. For Each child In node.childNodes
  2586. Select Case (GetElementName(child))
  2587. Case Empty
  2588. Case "DirectorySearch": If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessDirectorySearchElement(child, signature)
  2589. Case "FileSearch" : If fOneChild Then Fail "Only one search type can appear under Property: " & property Else fOneChild = True : signature = ProcessFileSearchElement (child, signature)
  2590. Case Else : Unexpected child, node
  2591. End Select
  2592. Next
  2593. ProcessDirectorySearchElement = signature
  2594. End Function ' ProcessDirectorySearchElement
  2595. Function ProcessFileSearchElement(node, parent)
  2596. Dim child, op, row, attribute, value, signature, fOneChild
  2597. Set row = installer.CreateRecord(UBound(SignatureTable))
  2598. For Each attribute In node.Attributes
  2599. value = attribute.value
  2600. Select Case(attribute.name)
  2601. Case "op" : op = value
  2602. Case "Name" : row.StringData (Signature_FileName) = value
  2603. Case "MinVersion" : row.StringData (Signature_MinVersion) = value
  2604. Case "MaxVersion" : row.StringData (Signature_MaxVersion) = value
  2605. Case "MinSize" : row.IntegerData(Signature_MinSize) = CLng(value)
  2606. Case "MaxSize" : row.IntegerData(Signature_MaxSize) = CLng(value)
  2607. Case "MinDate" : row.IntegerData(Signature_MinDate) = DosDate(value)
  2608. Case "MaxDate" : row.IntegerData(Signature_MaxDate) = DosDate(value)
  2609. Case "Languages" : row.StringData (Signature_Languages) = value
  2610. End Select
  2611. Next
  2612. If ElementHasText(node) Then
  2613. signature = ElementText(node)
  2614. Else
  2615. If Len(parent) = 0 Then Fail "Missing identifier for File search." Else signature = parent
  2616. End If
  2617. row.StringData (Signature_Signature) = Modularize(signature)
  2618. DoAction SignatureTable, op, row
  2619. ProcessFileSearchElement = signature
  2620. End Function ' ProcessFileSearchElement
  2621. '---------------------------------------------------------------------------------'
  2622. ' Module handlers
  2623. '---------------------------------------------------------------------------------'
  2624. Sub ProcessDependencyElement(node)
  2625. Dim op, row, attribute, value, required, requiredId, language, version
  2626. Set row = installer.CreateRecord(UBound(ModuleDependencyTable))
  2627. required = ElementText(node)
  2628. For Each attribute In node.Attributes
  2629. value = attribute.value
  2630. Select Case(attribute.name)
  2631. Case "op" : op = value
  2632. Case "Id" : requiredId = Replace(value, "-", "_")
  2633. Case "Version" : version = value
  2634. Case "Language" : language = CInt(value)
  2635. Case Else : Unexpected attribute, node
  2636. End Select
  2637. Next
  2638. If IsEmpty(language) Then language = 0
  2639. If Len(requiredId) <> 36 Then Fail "An Id must be specified for required Module"
  2640. row.StringData (ModuleDependency_ModuleID) = Modularize(productName)
  2641. row.IntegerData(ModuleDependency_ModuleLanguage) = CInt(productLanguage)
  2642. row.StringData (ModuleDependency_RequiredID) = required & "." & requiredId
  2643. row.IntegerData(ModuleDependency_RequiredLanguage) = language
  2644. row.StringData (ModuleDependency_RequiredVersion) = version
  2645. DoAction ModuleDependencyTable, op, row
  2646. End Sub
  2647. '---------------------------------------------------------------------------------'
  2648. ' Windows Installer 1.5 elements
  2649. '---------------------------------------------------------------------------------'
  2650. Sub ProcessAssemblyElement(node, sComponent, sFeature)
  2651. Dim op, row, child, attribute, value, sManifest, sApplication, nAttributes
  2652. Set row = installer.CreateRecord(UBound(MsiAssemblyTable))
  2653. For Each attribute in node.Attributes
  2654. value = attribute.value
  2655. Select Case(attribute.name)
  2656. Case "op" : op = value
  2657. Case "Manifest" : sManifest = value
  2658. Case "Application" : sApplication = value
  2659. Case "Type"
  2660. Select Case(value)
  2661. Case ".net" : nAttributes = 0
  2662. Case "win32" : nAttributes = 1
  2663. Case Else : Fail "Unknown Assembly.Type: " & value
  2664. End Select
  2665. Case Else : Unexpected child, node
  2666. End Select
  2667. Next
  2668. If IsEmpty(nAttributes) Then Fail "Must specify a 'Type' for <Assembly/>"
  2669. For Each child In node.childNodes
  2670. Select Case (GetElementName(child))
  2671. Case Empty
  2672. Case "Property" : ProcessAssemblyPropertyElement child, sComponent
  2673. Case Else : Unexpected child, node
  2674. End Select
  2675. Next
  2676. row.StringData(MsiAssembly_Component_) = Modularize(sComponent)
  2677. row.StringData(MsiAssembly_Feature_) = sFeature
  2678. row.StringData(MsiAssembly_File_Manifest) = Modularize(sManifest)
  2679. row.StringData(MsiAssembly_File_Application) = sApplication
  2680. row.IntegerData(MsiAssembly_Attributes) = nAttributes
  2681. DoAction MsiAssemblyTable, op, row
  2682. End Sub
  2683. Sub ProcessAssemblyPropertyElement(node, sComponent)
  2684. Dim op, row, child, attribute, value, sName, sValue
  2685. Set row = installer.CreateRecord(UBound(MsiAssemblyNameTable))
  2686. sName = ElementText(node)
  2687. For Each attribute in node.Attributes
  2688. value = attribute.value
  2689. Select Case(attribute.name)
  2690. Case "op" : op = value
  2691. Case "Value" : sValue = value
  2692. Case Else : Unexpected child, node
  2693. End Select
  2694. Next
  2695. If IsEmpty(sName) Then Fail "Must specify a 'Name' for <Assembly><Property/></Assembly>"
  2696. row.StringData(MsiAssemblyName_Component_) = Modularize(sComponent)
  2697. row.StringData(MsiAssemblyName_Name) = sName
  2698. row.StringData(MsiAssemblyName_Value) = sValue
  2699. DoAction MsiAssemblyNameTable, op, row
  2700. End Sub
  2701. Sub ProcessDigitalSignatureElement(node, sTable, sResource)
  2702. Dim op, row, child, attribute, value, src, sCertificate
  2703. Set row = installer.CreateRecord(UBound(MsiDigitalSignatureTable))
  2704. For Each attribute in node.Attributes
  2705. value = attribute.value
  2706. Select Case(attribute.name)
  2707. Case "op" : op = value
  2708. Case "src" : src = BaseDir(value)
  2709. End Select
  2710. Next
  2711. If ElementHasText(node) Then Fail "hex-encoded <DigitalSignature/> not currently supported"
  2712. If IsEmpty(src) Then Fail "Must specify a source file for DigitalSignature hash"
  2713. For Each child In node.childNodes
  2714. Select Case (GetElementName(child))
  2715. Case Empty
  2716. Case "DigitalCertificate" : If IsEmpty(sCertificate) Then sCertificate = ProcessDigitalCertificateElement(child) Else Fail "Only one <DigitalCertificate/> per <DigitalSignature/>"
  2717. Case Else : Unexpected child, node
  2718. End Select
  2719. Next
  2720. If IsEmpty(sCertificate) Then Fail "Must have a single <DigitalCertificate/> for a <DigitalSignature/>"
  2721. row.StringData(MsiDigitalSignature_Table) = sTable
  2722. row.StringData(MsiDigitalSignature_SignObject) = sResource
  2723. row.StringData(MsiDigitalSignature_DigitalCertificate_) = sCertificate
  2724. row.SetStream MsiDigitalSignature_Hash, src
  2725. DoAction MsiDigitalSignatureTable, op, row
  2726. End Sub
  2727. Function ProcessDigitalCertificateElement(node)
  2728. Dim op, row, child, attribute, value, sName, src
  2729. Set row = installer.CreateRecord(UBound(MsiDigitalCertificateTable))
  2730. For Each attribute in node.Attributes
  2731. value = attribute.value
  2732. Select Case(attribute.name)
  2733. Case "op" : op = value
  2734. Case "Name" : sName = value
  2735. Case "src" : src = BaseDir(value)
  2736. End Select
  2737. Next
  2738. If ElementHasText(node) Then Fail "hex-encoded <DigitalCertificate/> not currently supported"
  2739. If IsEmpty(sName) Then Fail "Must specify a 'Name' for <DigitalCertificate/>"
  2740. If IsEmpty(src) Then Fail "Must specify a 'src' file for <DigitalCertificate/>"
  2741. row.StringData(MsiDigitalCertificate_DigitalCertificate) = sName
  2742. row.SetStream MsiDigitalCertificate_CertData, src
  2743. DoAction MsiDigitalCertificateTable, op, row
  2744. ProcessDigitalCertificateElement = sName
  2745. End Function
  2746. '---------------------------------------------------------------------------------'
  2747. ' Patch element handlers
  2748. '---------------------------------------------------------------------------------'
  2749. Sub ProcessPatchElement(node)
  2750. If Not fNoOnError Then On Error Resume Next
  2751. Dim attribute, value
  2752. Dim sWholeFiles, sProductMismatches, sVersionMismatches, sClean
  2753. sWholeFiles = "0"
  2754. sProductMismatches = "0"
  2755. sVersionMismatches = "0"
  2756. sClean = "1"
  2757. sReplaceGUIDs = Empty
  2758. ' Walk XML nodes and populate .pcp tables
  2759. For Each attribute In node.Attributes
  2760. value = attribute.value
  2761. Select Case(attribute.name)
  2762. Case "Id" : value = "{"& value &"}" : ProcessProperties "PatchGUID", value, "replace"
  2763. Case "OutputPath" : ProcessProperties "PatchOutputPath", value, "replace"
  2764. Case "WholeFilesOnly" : If "yes" = value Then sWholeFiles = "1" Else sWholeFiles = "0"
  2765. Case "SourceList" : ProcessProperties "PatchSourceList", value, "replace"
  2766. Case "AllowProductCodeMismatches" : If "yes" = value Then sProductMismatches = "1" Else sProductMismatches = "0"
  2767. Case "AllowMajorVersionMismatches": If "yes" = value Then sVersionMismatches = "1" Else sVersionMismatches = "0"
  2768. Case "CleanWorkingFolder" : If "yes" = value Then sClean = "0" Else sClean = "1"
  2769. Case "OptionFlags" : ProcessProperties "ApiPatchingOptionFlags", value, "replace"
  2770. Case "SymbolFlags" : ProcessProperties "ApiPatchingSymbolFlags", value, "replace"
  2771. Case "xmlns" : ' ProcessProperties "XMLSchema", value, "replace"
  2772. Case Else : Unexpected attribute, node
  2773. End Select
  2774. Next
  2775. ProcessProperties "IncludeWholeFilesOnly", sWholeFiles, "replace"
  2776. ProcessProperties "AllowProductCodeMismatches", sProductMismatches, "replace"
  2777. ProcessProperties "AllowProductVersionMajorMismatches", sVersionMismatches, "replace"
  2778. ProcessProperties "DontRemoveTempFolderWhenFinished", sClean, "replace"
  2779. Dim child, sReplaceGUIDs, sTargetProducts
  2780. For Each child In node.childNodes
  2781. Select Case (GetElementName(child))
  2782. Case Empty
  2783. Case "Property" : ProcessPropertiesElement child
  2784. Case "Family" : ProcessFamilyElement child
  2785. Case "ReplacePatch" : sReplaceGUIDs = sReplaceGUIDs & "{" & ElementText(child) & "}"
  2786. Case "TargetProductCode"
  2787. value = ElementText(child)
  2788. If "*" <> value And 38 <> Len(value) Then Fail "Invalid TargetProductCode value: " & value
  2789. If Not IsEmpty(sTargetProducts) Then sTargetProducts = sTargetProducts & ";"
  2790. If "*" <> value Then value = "{" & value & "}"
  2791. sTargetProducts = sTargetProducts & value
  2792. Case Else : Unexpected child, node
  2793. End Select
  2794. Next
  2795. If Not IsEmpty(sReplaceGUIDs) Then ProcessProperties "ListOfPatchGUIDsToReplace", sReplaceGUIDs, "replace"
  2796. ProcessProperties "ListOfTargetProductCodes", sTargetProducts, "replace"
  2797. Set dictView = Nothing ' close all views, could also use RemoveAll method of Dictionary object
  2798. End Sub ' ProcessPatchElement
  2799. Sub ProcessProperties(sProperty, value, op)
  2800. Dim row : Set row = installer.CreateRecord(UBound(PropertiesTable))
  2801. row.StringData (Properties_Name) = sProperty
  2802. row.StringData (Properties_Value) = value
  2803. DoAction PropertiesTable, op, row
  2804. End Sub
  2805. Sub ProcessPropertiesElement(node)
  2806. Dim attribute, op, child, sProperty, value
  2807. sProperty = ElementText(node)
  2808. For Each attribute In node.Attributes
  2809. Select Case(attribute.name)
  2810. Case "op" : op = attribute.value
  2811. Case "Value" : value = attribute.value
  2812. Case Else : Unexpected attribute, node
  2813. End Select
  2814. Next
  2815. ProcessProperties sProperty, value, op
  2816. End Sub
  2817. Sub ProcessFamilyElement(node)
  2818. Dim op, row, attribute, value
  2819. Dim sFamily
  2820. Set row = installer.CreateRecord(UBound(ImageFamiliesTable))
  2821. sFamily = ElementText(node) ' !! maybe auto-generate?
  2822. For Each attribute In node.Attributes
  2823. value = attribute.value
  2824. Select Case(attribute.name)
  2825. Case "op" : op = value
  2826. Case "MediaSrcProp" : row.StringData(ImageFamilies_MediaSrcPropName) = value
  2827. Case "DiskId" : row.IntegerData(ImageFamilies_MediaDiskId) = CLng(value)
  2828. Case "SequenceStart": row.IntegerData(ImageFamilies_FileSequenceStart) = CLng(value)
  2829. Case "DiskPrompt" : row.StringData(ImageFamilies_DiskPrompt) = value
  2830. Case "VolumeLabel" : row.StringData(ImageFamilies_VolumeLabel) = value
  2831. Case Else : Unexpected attribute, node
  2832. End Select
  2833. Next
  2834. row.StringData(ImageFamilies_Family) = sFamily
  2835. DoAction ImageFamiliesTable, op, row
  2836. Dim child
  2837. For Each child In node.childNodes
  2838. Select Case (GetElementName(child))
  2839. Case Empty
  2840. Case "ExternalFile": ProcessExternalFileElement child, sFamily
  2841. Case "ProtectFile" : ProcessProtectFileElement child, sFamily
  2842. Case "UpgradeImage": ProcessUpgradeImageElement child, sFamily
  2843. Case Else : Unexpected child, node
  2844. End Select
  2845. Next
  2846. End Sub ' ProcessFamilyElement
  2847. Sub ProcessExternalFileElement(node, sFamily)
  2848. Dim op, row, attribute, value
  2849. Dim sFile, nOrder
  2850. Set row = installer.CreateRecord(UBound(ExternalFilesTable))
  2851. sFile = ElementText(node) ' !! maybe auto-generate?
  2852. For Each attribute In node.Attributes
  2853. value = attribute.value
  2854. Select Case(attribute.name)
  2855. Case "op" : op = value
  2856. Case "src" : row.StringData(ExternalFiles_FilePath) = BaseDir(value)
  2857. Case "Order" : nOrder = CLng(value)
  2858. Case Else : Unexpected attribute, node
  2859. End Select
  2860. Next
  2861. If IsEmpty(nOrder) Then nOrder = externalOrder : externalOrder = externalOrder + 1 Else externalOrder = nOrder + 1
  2862. row.StringData(ExternalFiles_Family) = sFamily
  2863. row.StringData(ExternalFiles_FTK) = sFile
  2864. row.IntegerData(ExternalFiles_Order) = nOrder
  2865. Dim child, sSymbols
  2866. Dim sProtectOffsets, sProtectLengths
  2867. Dim sIgnoreOffsets, sIgnoreLengths
  2868. For Each child In node.childNodes
  2869. Select Case (GetElementName(child))
  2870. Case Empty
  2871. Case "SymbolPaths" : If Not IsEmpty(sSymbols) Then sSymbols = sSymbols & ";" : sSymbols = sSymbols & BaseDir(ElementText(child))
  2872. Case "ProtectRange": ProcessRangeElement child, sProtectOffsets, sProtectLengths
  2873. Case "IgnoreRange" : ProcessRangeElement child, sIgnoreOffsets, sIgnoreLengths
  2874. Case Else : Unexpected child, node
  2875. End Select
  2876. Next
  2877. row.StringData(ExternalFiles_SymbolPaths) = sSymbols
  2878. row.StringData(ExternalFiles_IgnoreOffsets) = sIgnoreOffsets
  2879. row.StringData(ExternalFiles_IgnoreLengths) = sIgnoreLengths
  2880. If Not IsEmpty(sProtectOffsets) Then
  2881. row.StringData(ExternalFiles_RetainOffsets) = sProtectOffsets
  2882. Dim row2 : Set row2 = installer.CreateRecord(UBound(FamilyFileRangesTable))
  2883. row2.StringData(FamilyFileRanges_Family) = sFamily
  2884. row2.StringData(FamilyFileRanges_FTK) = sFile
  2885. row2.StringData(FamilyFileRanges_RetainOffsets) = sProtectOffsets
  2886. row2.StringData(FamilyFileRanges_RetainLengths) = sProtectLengths
  2887. DoAction FamilyFileRangesTable, "merge", row2
  2888. End If
  2889. DoAction ExternalFilesTable, op, row
  2890. End Sub ' ProcessExternalFileElement
  2891. Sub ProcessProtectFileElement(node, sFamily)
  2892. Dim op, row, attribute, value
  2893. Dim sFile
  2894. Set row = installer.CreateRecord(UBound(FamilyFileRangesTable))
  2895. sFile = ElementText(node) ' !! maybe auto-generate?
  2896. For Each attribute In node.Attributes
  2897. value = attribute.value
  2898. Select Case(attribute.name)
  2899. Case "op" : op = value
  2900. Case Else : Unexpected attribute, node
  2901. End Select
  2902. Next
  2903. row.StringData(FamilyFileRanges_Family) = sFamily
  2904. row.StringData(FamilyFileRanges_FTK) = sFile
  2905. Dim child
  2906. Dim sProtectOffsets, sProtectLengths
  2907. For Each child In node.childNodes
  2908. Select Case (GetElementName(child))
  2909. Case Empty
  2910. Case "ProtectRange" : ProcessRangeElement child, sProtectOffsets, sProtectLengths
  2911. Case Else : Unexpected child, node
  2912. End Select
  2913. Next
  2914. If IsEmpty(sProtectOffsets) Then Fail "Must specify Offsets for ProtectRange"
  2915. If IsEmpty(sProtectLengths) Then Fail "Must specify Lengths for ProtectRange"
  2916. row.StringData(FamilyFileRanges_RetainOffsets) = sProtectOffsets
  2917. row.StringData(FamilyFileRanges_RetainLengths) = sProtectLengths
  2918. DoAction FamilyFileRangesTable, op, row
  2919. End Sub ' ProcessProtectFileElement
  2920. Sub ProcessUpgradeImageElement(node, sFamily)
  2921. Dim op, row, attribute, value
  2922. Dim sUpgraded
  2923. Set row = installer.CreateRecord(UBound(UpgradedImagesTable))
  2924. sUpgraded = ElementText(node) ' !! maybe auto-generate?
  2925. For Each attribute In node.Attributes
  2926. value = attribute.value
  2927. Select Case(attribute.name)
  2928. Case "op" : op = value
  2929. Case "src" : row.StringData(UpgradedImages_MsiPath) = BaseDir(value)
  2930. Case "srcPatch": row.StringData(UpgradedImages_PatchMsiPath) = BaseDir(value)
  2931. Case Else : Unexpected attribute, node
  2932. End Select
  2933. Next
  2934. row.StringData(UpgradedImages_Upgraded) = sUpgraded
  2935. row.StringData(UpgradedImages_Family) = sFamily
  2936. Dim child, sSymbols
  2937. For Each child In node.childNodes
  2938. Select Case (GetElementName(child))
  2939. Case Empty
  2940. Case "SymbolPaths": If Not IsEmpty(sSymbols) Then sSymbols = sSymbols & ";" : sSymbols = sSymbols & BaseDir(ElementText(child))
  2941. Case "UpgradeFile": ProcessUpgradeFileElement child, sUpgraded
  2942. Case "TargetImage": ProcessTargetImageElement child, sUpgraded, sFamily
  2943. Case Else : Unexpected child, node
  2944. End Select
  2945. Next
  2946. row.StringData(UpgradedImages_SymbolPaths) = sSymbols
  2947. DoAction UpgradedImagesTable, op, row
  2948. End Sub ' ProcessUpgradeImageElement
  2949. Sub ProcessUpgradeFileElement(node, sUpgraded)
  2950. Dim op, row, attribute, value
  2951. Dim sFile, fIgnore, nAllowIgnore, nWholeFile
  2952. nAllowIgnore = 0
  2953. nWholeFile = 0
  2954. sFile = ElementText(node)
  2955. For Each attribute In node.Attributes
  2956. value = attribute.value
  2957. Select Case(attribute.name)
  2958. Case "op" : op = value
  2959. Case "Ignore" : If "yes" = value Then fIgnore = True Else fIgnore = False
  2960. Case "AllowIgnoreOnError": If "yes" = value Then nAllowIgnore = 1 Else nAllowIgnore = 0
  2961. Case "WholeFile" : If "yes" = value Then nWholeFile = 1 Else nWholeFile = 0
  2962. Case Else : Unexpected attribute, node
  2963. End Select
  2964. Next
  2965. If fIgnore Then
  2966. Set row = installer.CreateRecord(UBound(UpgradedFilesToIgnoreTable))
  2967. row.StringData(UpgradedFilesToIgnore_Upgraded) = sUpgraded
  2968. row.StringData(UpgradedFilesToIgnore_Upgraded) = sFile
  2969. DoAction UpgradedFilesToIgnoreTable, op, row
  2970. Else
  2971. Set row = installer.CreateRecord(UBound(UpgradedFiles_OptionalDataTable))
  2972. row.StringData(UpgradedFiles_OptionalDataTable_Upgraded) = sUpgraded
  2973. row.StringData(UpgradedFiles_OptionalDataTable_File) = sFile
  2974. row.IntegerData(UpgradedFiles_OptionalDataTable_AllowIgnoreOnPatchError) = nAllowIgnore
  2975. row.IntegerData(UpgradedFiles_OptionalDataTable_IncludeWholeFile) = nWholeFile
  2976. Dim child, sSymbols
  2977. For Each child In node.childNodes
  2978. Select Case (GetElementName(child))
  2979. Case Empty
  2980. Case "SymbolPaths": If Not IsEmpty(sSymbols) Then sSymbols = sSymbols & ";" : sSymbols = sSymbols & BaseDir(ElementText(child))
  2981. Case Else : Unexpected child, node
  2982. End Select
  2983. Next
  2984. row.StringData(UpgradedFiles_OptionalDataTable_SymbolPaths) = sSymbols
  2985. DoAction UpgradedFiles_OptionalDataTable, op, row
  2986. End If
  2987. End Sub ' ProcessUpgradeFileElement
  2988. Sub ProcessTargetImageElement(node, sUpgraded, sFamily)
  2989. Dim op, row, attribute, value
  2990. Dim sTarget, nIgnore, nOrder
  2991. nIgnore = 0
  2992. Set row = installer.CreateRecord(UBound(TargetImagesTable))
  2993. sTarget = ElementText(node) ' !! maybe auto-generate?
  2994. For Each attribute In node.Attributes
  2995. value = attribute.value
  2996. Select Case(attribute.name)
  2997. Case "op" : op = value
  2998. Case "src" : row.StringData(TargetImages_MsiPath) = BaseDir(value)
  2999. Case "Order" : nOrder = CLng(value)
  3000. Case "Validation": row.StringData(TargetImages_ProductValidateFlags) = value
  3001. Case "IgnoreMissingFiles": If "yes" = value Then nIgnore = 1 Else nIgnore = 0
  3002. Case Else : Unexpected attribute, node
  3003. End Select
  3004. Next
  3005. If IsEmpty(nOrder) Then nOrder = patchOrder : patchOrder = patchOrder + 1 Else patchOrder = nOrder + 1
  3006. row.StringData(TargetImages_Target) = sTarget
  3007. row.StringData(TargetImages_Upgraded) = sUpgraded
  3008. row.IntegerData(TargetImages_IgnoreMissingSrcFiles) = nIgnore
  3009. row.IntegerData(TargetImages_Order) = nOrder
  3010. Dim child, sSymbols
  3011. For Each child In node.childNodes
  3012. Select Case (GetElementName(child))
  3013. Case Empty
  3014. Case "SymbolPaths": If Not IsEmpty(sSymbols) Then sSymbols = sSymbols & ";" : sSymbols = sSymbols & BaseDir(ElementText(child))
  3015. Case "TargetFile": ProcessTargetFileElement child, sTarget, sFamily
  3016. Case Else : Unexpected child, node
  3017. End Select
  3018. Next
  3019. row.StringData(TargetImages_SymbolPaths) = sSymbols
  3020. DoAction TargetImagesTable, op, row
  3021. End Sub ' ProcessTargetImageElement
  3022. Sub ProcessTargetFileElement(node, sTarget, sFamily)
  3023. Dim op, row, attribute, value
  3024. Dim sFile, nIgnore
  3025. nIgnore = 0
  3026. Set row = installer.CreateRecord(UBound(TargetImagesTable))
  3027. sTarget = ElementText(node) ' !! maybe auto-generate?
  3028. For Each attribute In node.Attributes
  3029. value = attribute.value
  3030. Select Case(attribute.name)
  3031. Case "op" : op = value
  3032. Case Else : Unexpected attribute, node
  3033. End Select
  3034. Next
  3035. row.StringData(TargetFiles_OptionalData_Target) = sTarget
  3036. row.StringData(TargetFiles_OptionalData_FTK) = sFile
  3037. Dim child, sSymbols
  3038. Dim sProtectOffsets, sProtectLengths
  3039. Dim sIgnoreOffsets, sIgnoreLengths
  3040. For Each child In node.childNodes
  3041. Select Case (GetElementName(child))
  3042. Case Empty
  3043. Case "SymbolPaths" : If Not IsEmpty(sSymbols) Then sSymbols = sSymbols & ";" : sSymbols = sSymbols & BaseDir(ElementText(child))
  3044. Case "ProtectRange": ProcessRangeElement child, sProtectOffsets, sProtectLengths
  3045. Case "IgnoreRange" : ProcessRangeElement child, sIgnoreOffsets, sIgnoreLengths
  3046. Case Else : Unexpected child, node
  3047. End Select
  3048. Next
  3049. row.StringData(TargetFiles_OptionalData_SymbolPaths) = sSymbols
  3050. row.StringData(TargetFiles_OptionalData_IgnoreOffsets) = sIgnoreOffsets
  3051. row.StringData(TargetFiles_OptionalData_IgnoreLengths) = sIgnoreLengths
  3052. If Not IsEmpty(sProtectOffsets) Then
  3053. row.StringData(TargetFiles_OptionalData_RetainOffsets) = sProtectOffsets
  3054. Dim row2 : Set row2 = installer.CreateRecord(UBound(FamilyFileRangesTable))
  3055. row2.StringData(FamilyFileRanges_Family) = sFamily
  3056. row2.StringData(FamilyFileRanges_FTK) = sFile
  3057. row2.StringData(FamilyFileRanges_RetainOffsets) = sProtectOffsets
  3058. row2.StringData(FamilyFileRanges_RetainLengths) = sProtectLengths
  3059. DoAction FamilyFileRangesTable, "insert", row2
  3060. End If
  3061. DoAction TargetImagesTable, op, row
  3062. End Sub ' ProcessTargetImageElement
  3063. Sub ProcessRangeElement(node, ByRef sOffsets, ByRef sLengths)
  3064. Dim op, row, attribute, value, sOffset, sLength
  3065. For Each attribute In node.Attributes
  3066. value = attribute.value
  3067. Select Case(attribute.name)
  3068. Case "Offset" : sOffset = value
  3069. Case "Length" : sLength = value
  3070. Case Else : Unexpected attribute, node
  3071. End Select
  3072. Next
  3073. If IsEmpty(sOffset) Then Fail "Range missing Offset"
  3074. If IsEmpty(sLength) Then Fail "Range missing Length"
  3075. If Not IsEmpty(sOffsets) Then sOffsets = sOffsets & ","
  3076. If Not IsEmpty(sLengths) Then sLengths = sLengths & ","
  3077. sOffsets = sOffsets & sOffset
  3078. sLengths = sLengths & sLength
  3079. End Sub
  3080. '---------------------------------------------------------------------------------'
  3081. ' UI element handlers
  3082. '---------------------------------------------------------------------------------'
  3083. Sub ProcessUIElement(node)
  3084. Dim child
  3085. For Each child In node.childNodes
  3086. Select Case (GetElementName(child))
  3087. Case Empty
  3088. Case "Error" : ProcessErrorElement child
  3089. Case "ProgressText" : ProcessActionTextElement child
  3090. Case "Dialog" : ProcessDialogElement child
  3091. Case "TextStyle" : ProcessTextStyleElement child
  3092. Case "UIText" : ProcessUITextElement child
  3093. Case "BillboardAction" : ProcessBillboardActionElement child
  3094. Case "ListBox" : ProcessControlGroupElement child, ListBoxTable, "ListItem"
  3095. Case "ComboBox" : ProcessControlGroupElement child, ComboBoxTable, "ListItem"
  3096. Case "ListView" : ProcessControlGroupElement child, ListViewTable, "ListItem"
  3097. Case "RadioGroup" : ProcessControlGroupElement child, RadioButtonTable, "RadioButton"
  3098. ' the following are available indentically under the UI and Programs tabs for document organization use only
  3099. Case "Property" : ProcessPropertyElement child
  3100. Case "InstallUISequence" : ProcessSequence InstallUISequenceTable, child
  3101. Case "AdminUISequence" : ProcessSequence AdminUISequenceTable, child
  3102. Case "AdvertiseUISequence" : ProcessSequence AdvtUISequenceTable, child
  3103. Case "Binary" : ProcessBinaryElement child, BinaryTable
  3104. Case Else : Unexpected child, node
  3105. End Select
  3106. Next
  3107. End Sub
  3108. Sub ProcessListItemElement(node, table, property, op, order)
  3109. Dim attribute, row, text, icon
  3110. order = order + 1
  3111. Set row = installer.CreateRecord(UBound(table))
  3112. For Each attribute In node.Attributes
  3113. Select Case(attribute.name)
  3114. Case "op" : op = attribute.value
  3115. Case "Text" : text = attribute.value
  3116. Case "Icon" : If row.FieldCount = ListView_Binary_ Then icon = attribute.value Else Unexpected attribute, node
  3117. Case Else : Unexpected attribute, node
  3118. End Select
  3119. Next
  3120. row.StringData (ListView_Property) = Modularize(property)
  3121. row.IntegerData(ListView_Order) = order
  3122. row.StringData (ListView_Value) = ElementText(node)
  3123. row.StringData (ListView_Text) = text
  3124. If Len(icon) <> 0 Then row.StringData (ListView_Binary_) = Modularize(icon)
  3125. DoAction table, op, row
  3126. End Sub
  3127. Sub ProcessRadioButtonElement(node, property, op, order)
  3128. Dim attribute, value, row, tooltip, help
  3129. order = order + 1
  3130. Set row = installer.CreateRecord(UBound(RadioButtonTable))
  3131. For Each attribute In node.Attributes
  3132. value = attribute.value
  3133. Select Case(attribute.name)
  3134. Case "op" : op = value
  3135. Case "X" : row.IntegerData(RadioButton_X) = CInt(value)
  3136. Case "Y" : row.IntegerData(RadioButton_Y) = CInt(value)
  3137. Case "Width" : row.IntegerData(RadioButton_Width) = CInt(value)
  3138. Case "Height" : row.IntegerData(RadioButton_Height) = CInt(value)
  3139. Case "Text" : row.StringData (RadioButton_Text) = value
  3140. Case "Icon" : row.StringData (RadioButton_Text) = value
  3141. Case "ToolTip" : tooltip = value
  3142. Case "Help" : help = value
  3143. Case Else : Unexpected attribute, node
  3144. End Select
  3145. Next
  3146. row.StringData (RadioButton_Property) = Modularize(property)
  3147. row.IntegerData(RadioButton_Order) = order
  3148. If Len(tooltip) + Len(help) Then
  3149. row.StringData (RadioButton_Help) = tooltip & "|" & help
  3150. End If
  3151. row.StringData (RadioButton_Value) = ElementText(node)
  3152. DoAction RadioButtonTable, op, row
  3153. End Sub
  3154. Sub ProcessBillboardGroupElement(node)
  3155. Dim attribute, child, op, row, action, feature, order, billboard, grandchild
  3156. Set row = installer.CreateRecord(UBound(BillboardTable))
  3157. action = ElementText(node)
  3158. For Each attribute In node.Attributes
  3159. Unexpected attribute, node ' grouping element only, no attributes
  3160. Next
  3161. For Each child In node.childNodes
  3162. If GetElementName(child) <> "Billboard" Then Unexpected child, node
  3163. order = order + 1
  3164. billboard = ElementText(child)
  3165. For Each attribute In child.Attributes
  3166. Select Case(attribute.name)
  3167. Case "op" : op = attribute.value
  3168. Case "Feature" : feature = attribute.value
  3169. Case Else Unexpected attribute, child
  3170. End Select
  3171. Next
  3172. row.StringData (Billboard_Billboard) = billboard
  3173. row.StringData (Billboard_Feature_) = feature
  3174. row.StringData (Billboard_Action) = action
  3175. row.IntegerData(Billboard_Ordering) = order
  3176. DoAction BillboardTable, op, row
  3177. For Each grandchild in child.childNodes
  3178. If GetElementName(grandchild) <> "Control" Then Unexpected grandchild, child
  3179. ProcessControlElement grandchild, billboard, BBControlTable, Empty, Empty, Empty, Empty
  3180. Next
  3181. Next
  3182. End Sub
  3183. Sub ProcessControlGroupElement(node, table, childTag)
  3184. Dim attribute, op, child, property, order, value, childName
  3185. For Each attribute In node.Attributes
  3186. Select Case(attribute.name)
  3187. Case "op" : op = attribute.value
  3188. Case "Property" : property = attribute.value
  3189. End Select
  3190. Next
  3191. For Each child In node.childNodes
  3192. childName = GetElementName(child)
  3193. If Not IsEmpty(childName) Then
  3194. If childName <> childTag Then Unexpected child, node
  3195. Select Case (childName)
  3196. Case "ListItem" : ProcessListItemElement child, table, property, op, order
  3197. Case "RadioButton" : ProcessRadioButtonElement child, property, op, order
  3198. End Select
  3199. End If
  3200. Next
  3201. End Sub
  3202. Sub ProcessErrorElement(node)
  3203. Dim attribute, op, row, id
  3204. Set row = installer.CreateRecord(UBound(ErrorTable))
  3205. For Each attribute In node.Attributes
  3206. Select Case(attribute.name)
  3207. Case "op" : op = attribute.value
  3208. Case "Id" : id = CInt(attribute.value)
  3209. Case Else : Unexpected attribute, node
  3210. End Select
  3211. Next
  3212. row.IntegerData(Error_Error) = id
  3213. row.StringData (Error_Message) = ElementText(node)
  3214. DoAction ErrorTable, op, row
  3215. End Sub
  3216. Sub ProcessActionTextElement(node)
  3217. Dim attribute, op, row, id
  3218. Set row = installer.CreateRecord(UBound(ActionTextTable))
  3219. For Each attribute In node.Attributes
  3220. Select Case(attribute.name)
  3221. Case "op" : op = attribute.value
  3222. Case "Action" : row.StringData (ActionText_Action) = attribute.value
  3223. Case "Template" : row.StringData (ActionText_Template) = attribute.value
  3224. Case Else : Unexpected attribute, node
  3225. End Select
  3226. Next
  3227. REM Dim child : Set child = node.selectSingleNode("text()")
  3228. REM If Not child Is Nothing Then row.StringData (ActionText_Description) = child.text
  3229. row.StringData (ActionText_Description) = ElementText(node)
  3230. DoAction ActionTextTable, op, row
  3231. End Sub
  3232. Sub ProcessTableElement(node, table, attributes)
  3233. Dim attribute, op, row, id, index
  3234. Set row = installer.CreateRecord(UBound(table))
  3235. For Each attribute In node.Attributes
  3236. If attribute.name = "op" Then
  3237. op = attribute.value
  3238. Else
  3239. For index = 1 To UBound(table)
  3240. If attributes(index) = attribute.name Then
  3241. If InStr(table(index), "CHAR") > 0 Then
  3242. row.StringData(index) = attribute.value
  3243. Else
  3244. row.IntegerData(index) = CLng(attribute.value)
  3245. End If
  3246. End If
  3247. Next
  3248. If index > UBound(table) Then Unexpected attribute, node
  3249. End If
  3250. Next
  3251. For index = 1 To UBound(table)
  3252. If IsEmpty(attributes(index)) Then row.StringData(index) = ElementText(node)
  3253. Next
  3254. DoAction table, op, row
  3255. End Sub
  3256. Sub ProcessUITextElement(node)
  3257. Dim attribute, op, row, child, text
  3258. Set row = installer.CreateRecord(UBound(UITextTable))
  3259. For Each attribute In node.Attributes
  3260. Select Case(attribute.name)
  3261. Case "op" : op = attribute.value
  3262. Case "Text" : text = attribute.value
  3263. Case Else : Unexpected attribute, child
  3264. End Select
  3265. Next
  3266. For Each child In node.childNodes
  3267. Select Case (GetElementName(child))
  3268. Case Empty
  3269. Case "Text" : text = ElementText(child)
  3270. Case Else : Unexpected child, node
  3271. End Select
  3272. Next
  3273. row.StringData (UIText_Key) = Modularize(ElementText(node))
  3274. row.StringData (UIText_Text) = text
  3275. DoAction UITextTable, op, row
  3276. End Sub
  3277. Sub ProcessTextStyleElement(node)
  3278. Dim attribute, value, op, row, bits, color
  3279. Set row = installer.CreateRecord(UBound(TextStyleTable))
  3280. For Each attribute In node.Attributes
  3281. value = attribute.value
  3282. Select Case(attribute.name)
  3283. Case "op" : op = value
  3284. Case "Red" : color = color + CInt(attribute.value)
  3285. Case "Green" : color = color + CInt(attribute.value) * 256
  3286. Case "Blue" : color = color + CInt(attribute.value) * 65536
  3287. Case "Bold" : If value = "yes" Then bits = bits Or 1
  3288. Case "Italic" : If value = "yes" Then bits = bits Or 2
  3289. Case "Underline" : If value = "yes" Then bits = bits Or 4
  3290. Case "Strike" : If value = "yes" Then bits = bits Or 8
  3291. Case "FaceName" : row.StringData (TextStyle_FaceName) = value
  3292. Case "Size" : row.IntegerData(TextStyle_Size) = CInt(value)
  3293. Case Else : Unexpected attribute, node
  3294. End Select
  3295. Next
  3296. row.StringData (TextStyle_TextStyle) = ElementText(node) '!!! BUG: Must end in _UL so can you modularize?
  3297. If Not IsEmpty(color) Then row.IntegerData(TextStyle_Color) = color
  3298. If Not IsEmpty(bits) Then row.IntegerData(TextStyle_StyleBits) = bits
  3299. DoAction TextStyleTable, op, row
  3300. End Sub
  3301. Sub ProcessDialogElement(node)
  3302. Dim child, value, attribute, row, bits, op
  3303. Dim dialog, control, firstControl, defaultControl, cancelControl
  3304. Dim x,y : x = 50 : y = 50
  3305. Set row = installer.CreateRecord(UBound(DialogTable))
  3306. bits = msidbDialogAttributesVisible + msidbDialogAttributesModal + msidbDialogAttributesMinimize
  3307. dialog = ElementText(node)
  3308. For Each attribute In node.Attributes
  3309. value = attribute.value
  3310. Select Case(attribute.name)
  3311. Case "op" : op = value
  3312. Case "X" : x = CInt(value)
  3313. Case "Y" : y = CInt(value)
  3314. Case "Width" : row.IntegerData(Dialog_Width) = CInt(value)
  3315. Case "Height" : row.IntegerData(Dialog_Height) = CInt(value)
  3316. Case "Title" : row.StringData (Dialog_Title) = value
  3317. Case "Hidden" : If value="yes" Then bits = bits Xor msidbDialogAttributesVisible
  3318. Case "Modeless" : If value="yes" Then bits = bits Xor msidbDialogAttributesModal
  3319. Case "NoMinimize" : If value="yes" Then bits = bits Xor msidbDialogAttributesMinimize
  3320. Case "SystemModal" : If value="yes" Then bits = bits Xor msidbDialogAttributesSysModal
  3321. Case "KeepModeless" : If value="yes" Then bits = bits Xor msidbDialogAttributesKeepModeless
  3322. Case "TrackDiskSpace" : If value="yes" Then bits = bits Xor msidbDialogAttributesTrackDiskSpace
  3323. Case "CustomPalette" : If value="yes" Then bits = bits Xor msidbDialogAttributesUseCustomPalette
  3324. Case "RightToLeft" : If value="yes" Then bits = bits Xor msidbDialogAttributesRTLRO
  3325. Case "RightAligned" : If value="yes" Then bits = bits Xor msidbDialogAttributesRightAligned
  3326. Case "LeftScroll" : If value="yes" Then bits = bits Xor msidbDialogAttributesLeftScroll
  3327. Case "ErrorDialog" : If value="yes" Then bits = bits Xor msidbDialogAttributesError
  3328. Case Else : Unexpected attribute, node
  3329. End Select
  3330. Next
  3331. Dim lastTabRow : Set lastTabRow = Nothing
  3332. For Each child In node.childNodes
  3333. Select Case (GetElementName(child))
  3334. Case Empty
  3335. Case "Control" ProcessControlElement child, dialog, ControlTable, lastTabRow, firstControl, defaultControl, cancelControl
  3336. Case Else : Unexpected child, node
  3337. End Select
  3338. Next
  3339. Set child = Nothing
  3340. ProcessControlElement child, dialog, ControlTable, lastTabRow, firstControl, defaultControl, cancelControl
  3341. row.StringData (Dialog_Dialog) = Modularize(dialog)
  3342. row.IntegerData(Dialog_HCentering) = x
  3343. row.IntegerData(Dialog_VCentering) = y
  3344. row.IntegerData(Dialog_Attributes) = bits
  3345. row.StringData (Dialog_Control_First) = Modularize(firstControl)
  3346. row.StringData (Dialog_Control_Default) = Modularize(defaultControl)
  3347. row.StringData (Dialog_Control_Cancel) = Modularize(cancelControl)
  3348. DoAction DialogTable, op, row
  3349. End Sub
  3350. Sub ProcessControlElement(node, dialog, table, lastTabRow, firstControl, defaultControl, cancelControl)
  3351. Dim child, value, attribute, row, op
  3352. Dim control, noTab, controlType, specialAttributes, bit, bits, publishOrder, text, property, help, disabled
  3353. Dim checkboxValue, checkboxRow
  3354. Dim x,y : x = 50 : y = 50
  3355. If node Is Nothing Then ' called at exit of Dialog child processing loop to force out cached row
  3356. If Not lastTabRow Is Nothing Then
  3357. If lastTabRow.StringData(Control_Control) <> firstControl Then
  3358. lastTabRow.StringData(Control_Control_Next) = Modularize(firstControl)
  3359. End If
  3360. DoAction ControlTable, op, lastTabRow
  3361. End If
  3362. Exit Sub ' last time through
  3363. End If
  3364. control = ElementText(node)
  3365. Set row = installer.CreateRecord(UBound(table))
  3366. controlType = node.Attributes.GetNamedItem("Type").value ' need to get first
  3367. Select Case(controlType)
  3368. Case "Text" : specialAttributes = textControlAttributes : If Not IsEmpty(firstControl) Then noTab = True
  3369. Case "Edit" : specialAttributes = editControlAttributes
  3370. Case "MaskedEdit" : specialAttributes = editControlAttributes
  3371. Case "PathEdit" : specialAttributes = editControlAttributes
  3372. Case "Icon" : specialAttributes = iconControlAttributes : noTab = True : disabled = True
  3373. Case "Bitmap" : specialAttributes = bitmapControlAttributes : noTab = True : disabled = True
  3374. Case "ProgressBar" : specialAttributes = progressControlAttributes : noTab = True : disabled = True
  3375. Case "DirectoryCombo" : specialAttributes = volumeControlAttributes
  3376. Case "VolumeSelectCombo" : specialAttributes = volumeControlAttributes
  3377. Case "VolumeCostList" : specialAttributes = volumeControlAttributes : noTab = True
  3378. Case "ListBox" : specialAttributes = listboxControlAttributes
  3379. Case "ListView" : specialAttributes = listviewControlAttributes
  3380. Case "ComboBox" : specialAttributes = comboboxControlAttributes
  3381. Case "PushButton" : specialAttributes = buttonControlAttributes
  3382. Case "CheckBox" : specialAttributes = checkboxControlAttributes
  3383. Case "RadioButtonGroup" : specialAttributes = radioControlAttributes
  3384. Case "ScrollableText" : specialAttributes = Array()
  3385. Case "SelectionTree" : specialAttributes = Array()
  3386. Case "DirectoryList" : specialAttributes = Array()
  3387. Case "GroupBox" : specialAttributes = Array() : noTab = True : disabled = True
  3388. Case "Line" : specialAttributes = Array() : noTab = True : disabled = True
  3389. Case "Billboard" : specialAttributes = Array() : noTab = True : disabled = True
  3390. Case Else : specialAttributes = Array() : noTab = True
  3391. End Select
  3392. If disabled Then bits = msidbControlAttributesEnabled ' bit will be inverted when stored
  3393. For Each attribute In node.Attributes
  3394. value = attribute.value
  3395. Select Case(attribute.name)
  3396. Case "Type" ' already processed
  3397. Case "op" : op = value
  3398. Case "Default" : If value = "yes" Then defaultControl = control
  3399. Case "Cancel" : If value = "yes" Then cancelControl = control
  3400. Case "TabSkip" : If value = "yes" Then noTab = True Else If value = "no" Then notab = False
  3401. Case "X" : x = CInt(value)
  3402. Case "Y" : y = CInt(value)
  3403. Case "Text" : text = value
  3404. Case "Property" : property = value
  3405. Case "Help" : help = value
  3406. Case "Width" : row.IntegerData(Control_Width) = CInt(value)
  3407. Case "Height" : row.IntegerData(Control_Height) = CInt(value)
  3408. Case "CheckBoxValue" : checkboxValue = value
  3409. Case "IconSize"
  3410. Select Case(value)
  3411. Case "16" : bit = NameToBit(specialAttributes, "Icon16", "yes")
  3412. Case "32" : bit = NameToBit(specialAttributes, "Icon32", "yes")
  3413. Case "48" : bit = (NameToBit(specialAttributes, "Icon16", "yes") Or NameToBit(specialAttributes, "Icon32", "yes"))
  3414. Case Else : Fail "Invalid IconSize: " & value
  3415. End Select
  3416. If IsEmpty(bit) Then Unexpected attribute, node
  3417. bits = bits Xor (bit * 65536)
  3418. Case Else
  3419. bit = NameToBit(commonControlAttributes, attribute.name, value)
  3420. If IsEmpty(bit) Then bit = NameToBit(specialAttributes, attribute.name, value) * 65536
  3421. If IsEmpty(bit) Then Unexpected attribute, node
  3422. bits = bits Xor bit
  3423. End Select
  3424. Next
  3425. For Each child In node.childNodes
  3426. Select Case (GetElementName(child))
  3427. Case Empty
  3428. Case "Text" : text = ElementText(child)
  3429. Case "Condition" : ProcessControlConditionElement child, dialog, control
  3430. Case "Publish" : ProcessPublishElement child, dialog, control, publishOrder
  3431. Case "Subscribe" : ProcessSubscribeElement child, dialog, control
  3432. Case Else : Unexpected child, node
  3433. End Select
  3434. Next
  3435. row.StringData (Control_Dialog_) = Modularize(dialog)
  3436. row.StringData (Control_Control) = Modularize(control)
  3437. row.StringData (Control_Type) = controlType
  3438. row.IntegerData(Control_X) = x
  3439. row.IntegerData(Control_Y) = y
  3440. row.IntegerData(Control_Attributes) = bits Xor (msidbControlAttributesVisible Or msidbControlAttributesEnabled)
  3441. If IsEmpty(lastTabRow) Then ' Billboard control
  3442. row.StringData (BBControl_Text) = text
  3443. Else
  3444. row.StringData (Control_Text) = text
  3445. row.StringData (Control_Property) = property
  3446. row.StringData (Control_Help) = help
  3447. End If
  3448. If noTab Then
  3449. DoAction table, op, row
  3450. Else
  3451. If IsEmpty(lastTabRow) Then Fail "Tabbable Control not allowed in Billboard: " & controlType
  3452. If IsEmpty(firstControl) Then firstControl = control
  3453. If Not lastTabRow Is Nothing Then
  3454. lastTabRow.StringData(Control_Control_Next) = control
  3455. DoAction ControlTable, op, lastTabRow
  3456. End If
  3457. Set lastTabRow = row
  3458. End If
  3459. If Not IsEmpty(checkBoxValue) Then
  3460. If controlType <> "CheckBox" Then Fail "CheckBoxValue attribute valid only with CheckBox"
  3461. Set checkboxRow = installer.CreateRecord(UBound(CheckBoxTable))
  3462. checkboxRow.StringData(CheckBox_Property) = Modularize(property)
  3463. checkboxRow.StringData(CheckBox_Value) = checkboxValue
  3464. DoAction CheckBoxTable, op, checkboxRow
  3465. End If
  3466. End Sub
  3467. Sub ProcessControlConditionElement(node, dialog, control)
  3468. Dim attribute, value, op, row, id, action
  3469. Set row = installer.CreateRecord(UBound(ControlConditionTable))
  3470. For Each attribute In node.Attributes
  3471. value = attribute.value
  3472. Select Case(attribute.name)
  3473. Case "op" : op = value
  3474. Case "Action" : action = UCase(Left(value,1)) & Right(value, Len(value)-1)
  3475. Case Else : Unexpected attribute, node
  3476. End Select
  3477. Next
  3478. row.StringData (ControlCondition_Dialog_) = Modularize(dialog)
  3479. row.StringData (ControlCondition_Control_) = Modularize(control)
  3480. row.StringData (ControlCondition_Action) = action
  3481. row.StringData (ControlCondition_Condition) = ElementText(node)
  3482. DoAction ControlConditionTable, op, row
  3483. End Sub
  3484. Sub ProcessPublishElement(node, dialog, control, order)
  3485. Dim attribute, value, op, row, id, event_, argument
  3486. Set row = installer.CreateRecord(UBound(ControlEventTable))
  3487. order = order + 1
  3488. For Each attribute In node.Attributes
  3489. value = attribute.value
  3490. Select Case(attribute.name)
  3491. Case "op" : op = value
  3492. Case "Event" : event_ = UCase(Left(value,1)) & Right(value, Len(value)-1)
  3493. Case "Property" : event_ = "[" & value & "]"
  3494. Case "Value" : argument = value
  3495. Case Else : Unexpected attribute, node
  3496. End Select
  3497. Next
  3498. If IsEmpty(event_) Then event_ = "{}"
  3499. row.StringData (ControlEvent_Dialog_) = Modularize(dialog)
  3500. row.StringData (ControlEvent_Control_) = Modularize(control)
  3501. row.StringData (ControlEvent_Event) = Modularize(event_)
  3502. row.StringData (ControlEvent_Argument) = argument
  3503. row.IntegerData(ControlEvent_Ordering) = order
  3504. If ElementHasText(node) Then row.StringData (ControlEvent_Condition) = ElementText(node)
  3505. DoAction ControlEventTable, op, row
  3506. End Sub
  3507. Sub ProcessSubscribeElement(node, dialog, control)
  3508. Dim attribute, value, op, row, id, event_, controlAttribute
  3509. Set row = installer.CreateRecord(UBound(EventMappingTable))
  3510. For Each attribute In node.Attributes
  3511. value = attribute.value
  3512. Select Case(attribute.name)
  3513. Case "op" : op = value
  3514. Case "Event" : event_ = UCase(Left(value,1)) & Right(value, Len(value)-1)
  3515. Case "Attribute" : controlAttribute = UCase(Left(value,1)) & Right(value, Len(value)-1)
  3516. Case Else : Unexpected attribute, node
  3517. End Select
  3518. Next
  3519. row.StringData (EventMapping_Dialog_) = Modularize(dialog)
  3520. row.StringData (EventMapping_Control_) = Modularize(control)
  3521. row.StringData (EventMapping_Event) = Modularize(event_)
  3522. row.StringData (EventMapping_Attribute) = controlAttribute
  3523. DoAction EventMappingTable, op, row
  3524. End Sub
  3525. </script>
  3526. </job>