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

479 lines
16 KiB

  1. /*
  2. *
  3. *
  4. * This is the file you need to include to pull all the relevant
  5. * functionality for Sdb* functions
  6. * Note that despite api parameters appearing as TCHAR in definitions, they are always
  7. * WCHARs on NT and always CHARs on Win9x
  8. *
  9. */
  10. #include "shimdb.h"
  11. /*++
  12. XML how-to
  13. 1. db.xml in xml directory is a snapshot of our actual xml file
  14. (you should enlist into lab6 windows\published and windows\appcompat!!!)
  15. 2. Transforms(remedies) are specified under <LIBRARY> :
  16. <!-- MSI Transforms -->
  17. <!-- this is the file containing the transform -- it should be accessible to the compiler
  18. when appcompat database is being built -->
  19. <FILE NAME="notepad.exe"/>
  20. <!-- this is description stub, transform is referenced by name ExperimentalTransform
  21. refers to the filename notepad.exe -->
  22. <MSI_TRANSFORM NAME="ExperimentalTransform" FILE="notepad.exe">
  23. <DESCRIPTION>
  24. This is a sample transform for MSI implementation
  25. </DESCRIPTION>
  26. </MSI_TRANSFORM>
  27. 3. Packages are specified like this:
  28. <APP NAME="Some random app" VENDOR="ArtsCraftsAndCalamitiesInc">
  29. <DESCRIPTION>
  30. This is description
  31. </DESCRIPTION>
  32. ...
  33. <MSI_PACKAGE NAME="Test install 1" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
  34. <DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x12345678"/>
  35. <DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is my test string"/>
  36. <DATA NAME="Kozu44" VALUETYPE="BINARY">
  37. 01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
  38. </DATA>
  39. <MSI_TRANSFORM NAME="ExperimentalTransform"/>
  40. </MSI_PACKAGE>
  41. <MSI_PACKAGE NAME="Test Install 2 same guid" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
  42. <DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x45678"/>
  43. <DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is another string"/>
  44. <DATA NAME="Kozu44" VALUETYPE="BINARY">
  45. 01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
  46. </DATA>
  47. <MSI_TRANSFORM NAME="ExperimentalTransform2"/>
  48. </MSI_PACKAGE>
  49. </APP>
  50. 4. Compiling database - the best way to do it -- is to build sysmain.sdb using
  51. %sdxroot%\windows\appcompat\package
  52. to do it:
  53. a. Enlist into lab6 windows\appcompat and windows\published,
  54. build in windows\published then in windows\appcompat
  55. b. modify windows\appcompat\db\db.xml
  56. c. Copy all the msi transform files into %_NTTREE%\shimdll
  57. c. build in windows\appcompat\package
  58. d. appfix.exe package is ready for deployment in obj\i386
  59. ======================================================================================================
  60. New Features:
  61. 1. Nested DATA tags:
  62. <MSI_PACKAGE NAME="Test install 1" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
  63. <DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x12345678"/>
  64. <DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is my test string"/>
  65. <DATA NAME="Kozu44" VALUETYPE="BINARY">
  66. 01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
  67. </DATA>
  68. <DATA NAME="Root" VALUETYPE="STRING" VALUE="ROOT">
  69. <DATA NAME="Node1" VALUETYPE="DWORD" VALUE="0x1"/>
  70. <DATA NAME="Node2" VALUETYPE="DWORD" VALUE="0x2">
  71. <DATA NAME="SubNode1" VALUETYPE="DWORD" VALUE="0x3"/>
  72. <DATA NAME="SubNode2" VALUETYPE="DWORD" VALUE="0x4"/>
  73. </DATA>
  74. </DATA>
  75. <MSI_TRANSFORM NAME="ExperimentalTransform"/>
  76. </MSI_PACKAGE>
  77. 2. New API and enhanced old api to query nested data tags:
  78. - direct addressing:
  79. Status = SdbQueryData(hSDB,
  80. trMatch,
  81. L"Root\Node2\Subnode2", // <<<<< direct path to the value
  82. &dwDataType,
  83. NULL,
  84. &dwDataSize);
  85. - via enumeration
  86. Status = SdbQueryDataEx(hSDB,
  87. trMatch,
  88. L"Root",
  89. &dwDataType,
  90. NULL,
  91. &dwDataSize,
  92. &trDataRoot);
  93. the call above retrieves trDataRoot which allows for further calls to SdbQueryData/SdbQueryDataEx:
  94. Status = SdbQueryDataEx(hSDB,
  95. trDataRoot,
  96. L"Node2",
  97. &dwDataType,
  98. NULL,
  99. &dwDataSize,
  100. NULL); // <<< passing NULL is allowed
  101. 3. Fully functional custom databases.
  102. - compile db.xml that comprises your private database
  103. shimdbc fix -f .\MyMsiTransforms MyMsiPackages.xml MsiPrivate.sdb
  104. (above -f parameter points shimdbc to the directory which has all the transform files)
  105. - install the database using sdbInst (in tools directory)
  106. sdbinst MsiPrivate.sdb
  107. - Run query as usual -- the normal enumeration process will now include all the custom dbs in
  108. addition to any data located in the main database
  109. - You can unistall the db using sdbinst as well
  110. 4. New flag in SdbInitDatabase
  111. hSDB = SdbInitDatabase(HID_DATABASE_FULLPATH|HID_DOS_PATH, L"c:\\temp\\mydb.sdb")
  112. the flag HID_DATABASE_FULLPATH directs us to open the database specified as a second parameter and treat it as
  113. "main" database. You can also avoid opening ANY database at all -- and rely upon local database and/or custom database:
  114. hSDB = SdbInitDatabase(HID_NO_DB, NULL)
  115. 5. Note that NONE is a valid data type -
  116. <DATA NAME="Foo" VALUETYPE="NONE">
  117. </DATA>
  118. this entry is perfectly valid
  119. --*/
  120. DetectMsiPackage()
  121. {
  122. HSDB hSDB;
  123. SDBMSIFINDINFO MsiFindInfo;
  124. /*++
  125. General note:
  126. * While debugging your code it is useful to set SHIM_DEBUG_LEVEL environment variable
  127. * in order to see debug spew from Sdb* functions. To see all the debug output:
  128. * Set SHIM_DEBUG_LEVEL=9
  129. 1. Initialize Database
  130. Function:
  131. HSDB
  132. SdbInitDatabase(
  133. IN DWORD dwFlags, // flags that tell how the database should be
  134. // initialized.
  135. IN LPCTSTR pszDatabasePath // the OPTIONAL full path to the database to
  136. // be used.
  137. )
  138. dwFlags could have HID_DOS_PATH if you provide pszDatabasePath
  139. Function will try to open sysmain.sdb and systest.sdb (if available) in \SystemRoot\AppPatch if none of the
  140. parameters are supplied. To specify alternative location for sysmain.sdb and systest.sdb :
  141. SdbInitDatabase(HID_DOS_PATH, L"c:\foo") will look for sysmain.sdb in c:\foo
  142. --*/
  143. hSDB = SdbInitDatabase(0, NULL);
  144. if (NULL == hSDB) {
  145. //
  146. // failed to initialize database
  147. //
  148. }
  149. /*++
  150. 2. Detect this particular package -- there could be more than one match, caller should
  151. try and distinguish between the packages by reading supplemental data
  152. Detection is done by calling SdbFindFirstMsiPackage/SdbFindFirsMsiPackage_Str and
  153. SdbFindNextMsiPacakage
  154. Functions:
  155. TAGREF
  156. SdbFindFirstMsiPackage_Str(
  157. IN HSDB hSDB, // handle obtained in a call to SdbInitDatabase
  158. IN LPCTSTR lpszGuid, // guid in the form {xxxxx.... }
  159. IN LPCTSTR lpszLocalDB, // Optional full path to the local DB file
  160. OUT PSDBMSIFINDINFO pFindInfo // find context
  161. );
  162. TAGREF
  163. SdbFindNextMsiPackage(
  164. IN HSDB hSDB, // handle, see above
  165. IN OUT PSDBMSIFINDINFO pFindInfo // find context, previously obtained from SdbFindFirstMsiPackage* functions
  166. );
  167. --*/
  168. trMatch = SdbFindFirstMsiPackage_Str(hSDB,
  169. L"{740fa5d8-6472-4c36-9129-364b773fa64e}",
  170. NULL,
  171. &MsiFindInfo);
  172. while (TAGREF_NULL != trMatch) {
  173. /*++
  174. 3a.Examine name of this package. Use these functions:
  175. WCHAR wszBuffer[MAX_PATH];
  176. trName = SdbFindFirstTagRef(hSDB, trMatch, TAG_NAME);
  177. if (TAGREF_NULL != trName) {
  178. bSuccess = SdbReadStringTagRef(hSDB, trName, wszBuffer, sizeof(wszBuffer)/sizeof(wszBuffer[0]));
  179. }
  180. the code above reads "Special Install package" given the xml below:
  181. <!-- MSI transform -->
  182. <MSI_PACKAGE NAME="Special install package" ID="{740fa5d8-6472-4c36-9129-364b773fa64e}">
  183. <DATA NAME="Data1" VALUETYPE="DWORD" VALUE="0x12345678"/>
  184. <DATA NAME="Moo2" VALUETYPE="STRING" VALUE="This is my test string"/>
  185. <DATA NAME="Kozu44" VALUETYPE="BINARY">
  186. 01 23 45 67 89 10 11 12 13 14 15 16 17 18 19 20 21
  187. </DATA>
  188. <MSI_TRANSFORM NAME="ExperimentalTransform2"/>
  189. <MSI_TRANSFORM NAME="ExperimentalTransform3"/>
  190. </MSI_PACKAGE>
  191. 3b.Examine supplemental data for this package:
  192. DWORD
  193. SdbQueryData(
  194. IN HSDB hSDB, // database handle
  195. IN TAGREF trExe, // tagref of the matching exe
  196. IN LPCTSTR lpszDataName, // if this is null, will try to return all the policy names
  197. OUT LPDWORD lpdwDataType, // pointer to data type (REG_SZ, REG_BINARY, etc)
  198. OUT LPVOID lpBuffer, // buffer to fill with information
  199. IN OUT LPDWORD lpdwBufferSize // pointer to buffer size
  200. );
  201. //
  202. // Query for information - step 1 -- obtain all the available data for this package
  203. //
  204. Status = SdbQueryData(hSDB,
  205. trMatch,
  206. NULL, // data value name
  207. &dwDataType, // pointer to the data type
  208. NULL, // pointer to the buffer
  209. &dwDataSize);
  210. // expected return value:
  211. // ERROR_INSUFFICIENT_BUFFER
  212. // dwDataSize will contain the required buffer size in bytes
  213. //
  214. // assuming that we allocated dwDataSize bytes, pBuffer is the buffer pointer
  215. //
  216. Status = SdbQueryData(hSDB,
  217. trMatch,
  218. NULL,
  219. &dwDataType,
  220. pBuffer,
  221. &dwDataSize);
  222. //
  223. // expected return value:
  224. // ERROR_SUCCESS
  225. // dwDataSize will contain the number of bytes written into the buffer
  226. // dwDataType will contain REG_MULTI_SZ
  227. // pBuffer will receive the following data (unicode strings, assuming xml above was used)
  228. // Data1\0Moo2\0Kozu44\0\0
  229. //
  230. Status = SdbQueryData(hSDB,
  231. trMatch,
  232. L"Data1",
  233. &dwDataType,
  234. NULL,
  235. &dwDataSize);
  236. //
  237. // Expected return value:
  238. // ERROR_INSUFFICIENT_BUFFER
  239. // dwDataSize will contain 4
  240. // dwDataType will contain REG_DWORD
  241. //
  242. dwDataSize = sizeof(dwData);
  243. Status = SdbQueryData(hSDB,
  244. trMatch,
  245. L"Data1",
  246. &dwDataType,
  247. &dwData,
  248. &dwDataSize);
  249. // expected return value:
  250. // ERROR_SUCCESS
  251. // dwData will have a value of 0x12345678
  252. //
  253. .... etc ...
  254. --*/
  255. //
  256. // We presume that using the code above we determined that this package is a match, we break out of the loop
  257. // trMatch is the matching package
  258. //
  259. if (bMatch) {
  260. break;
  261. }
  262. trMatch = SdbFindNextMsiPackage(hSDB, &MsiFindInfo);
  263. }
  264. if (TAGREF_NULL == trMatch) {
  265. return; // no match
  266. }
  267. /*++
  268. 4. Seek the remedies. To do so there are two ways:
  269. - Enumerate all the transforms available for this package using
  270. DWORD // returns the error code, ERROR_SUCCESS if successful
  271. SdbEnumMsiTransforms(
  272. IN HSDB hSDB, // db handle
  273. IN TAGREF trMatch, // matching tagref for the package
  274. OUT TAGREF* ptrBuffer, // pointer to the buffer that will be filled with transform tagrefs
  275. IN OUT DWORD* pdwBufferSize // size of the buffer in bytes, upon return will be set to the number
  276. ); // of bytes written to the buffer
  277. dwError = SdbEnumMsiTransforms(hsdb, trMatch, NULL, &dwSize);
  278. //
  279. // expeceted ERROR_INSUFFICIENT_BUFFER,
  280. // allocate dwSize bytes
  281. //
  282. TAGREF* ptrBuffer = new TAGREF[dwSize/sizeof(TAGREF)];
  283. dwError = SdbEnumMsiTransforms(hsdb, trMatch, ptrBuffer, &dwSize);
  284. //
  285. // expected return: ERROR_SUCCESS
  286. //
  287. // ptrBuffer will have dwSize/sizeof(TAGREF) values filled with transform tagrefs
  288. for (i = 0; i < dwSize / sizeof(TAGREF); ++i) {
  289. trTransform = ptrBuffer[i];
  290. // see below what we do with trTransform
  291. }
  292. - Enumerate transforms one by one using macros:
  293. SdbGetFirstMsiTransformForPackage(hSDB, trMatch)
  294. SdbGetNextMsiTransformForPackage(hSDB, trMatch, trPrevTransform)
  295. trTransform = SdbGetFirstMsiTransformForPackage(hSDB, trMatch);
  296. while (trTransform != TAGREF_NULL) {
  297. //
  298. // see below what we do with trTansform
  299. //
  300. trTransform = SdbGetNextMsiTransformForPackage(hSDB, trMatch, trTransform);
  301. }
  302. 5. Read the transforms and extract transform files if available
  303. a. Read information:
  304. BOOL
  305. SdbReadMsiTransformInfo(
  306. IN HSDB hSDB, // db handle
  307. IN TAGREF trTransformRef, // trTransform obtained above
  308. OUT PSDBMSITRANSFORMINFO pTransformInfo // pointer to SDBMSITRANSFORMINFO structure
  309. );
  310. SDBMSITRANSFORMINFO MsiTransformInfo;
  311. //
  312. // Members of the structure:
  313. //
  314. // LPCWSTR lpszTransformName; // name of the transform
  315. // TAGREF trTransform; // tagref of this transform
  316. // TAGREF trFile; // tagref of file for this transform (bits)
  317. bSuccess = SdbReadMsiTransformInfo(hSDB, trTransform, &MsiTransformInfo);
  318. if (bSuccess) {
  319. //
  320. // MsiTransformInfo.lpszTransformName is the name of the transform (e.g. "ExperimentalTransform2"
  321. // from xml above. The bits are available for extraction if MsiTransformInfo.trFile != TAGREF_NULL
  322. //
  323. }
  324. b. Extract Transform bits:
  325. The structure pointed to by pTransformInfo should be obtained by calling SdbReadMsiTransformInfo
  326. BOOL
  327. SdbCreateMsiTransformFile(
  328. IN HSDB hSDB, // db handle
  329. IN LPCTSTR lpszFileName, // filename to write data to
  330. IN PSDBMSITRANSFORMINFO pTransformInfo // pointer to the transform structure
  331. );
  332. bSuccess = SdbCreateMsiTransformFile(hSDB, L"c:\foo\mytransform.msi", &MsiTransformInfo);
  333. --*/
  334. }