Leaked source code of windows server 2003
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.

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