{"id":2443,"date":"2006-02-14T19:46:00","date_gmt":"2006-02-14T19:46:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/heaths\/2006\/02\/14\/extract-files-from-patches\/"},"modified":"2006-02-14T19:46:00","modified_gmt":"2006-02-14T19:46:00","slug":"extract-files-from-patches","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/setup\/extract-files-from-patches\/","title":{"rendered":"Extract Files from Patches"},"content":{"rendered":"<p>From the mailbag, someone asked how to extract files from a patch. Now\npresumably one would want to extract the files as they apply to a product if the\npatch were installed but I will cover both ways because one can lead to the\nother. If you&#8217;re looking for the simplest and quickest way to extract files from\na patch skip toward the end; otherwise, if you&#8217;re interested in the structure of\na <i>.msp<\/i> file and how to extract all files regardless of a particular\nproduct the patch targets read on.<\/p>\n<p>Recall from\n<a href=\"\/heaths\/archive\/2005\/09\/01\/459561.aspx\">What&#8217;s in\na Patch<\/a> that a <i>.msp<\/i> file contains sub-storages for pairs of\ntransforms that transform the patch target package to the patch upgrade package, and\npossibly one or more sub-streams for the cabinet files that contain the files to\nbe patched. Because the internal structure of a <i>.msp<\/i> file uses OLE structured\nstorage you can extract the transforms and cabinet files out; however, to allow\nfor 72 characters instead of 36 characters as\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/storage_object_naming_conventions.asp\">limited<\/a> by OLE, Windows Installer\ncompresses stream names except for the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/summary_information_stream.asp\">\nsummary information stream<\/a>, named 05SummaryInformation. You&#8217;ll also find\nmore streams than perhaps expected for use by Windows Installer. That doesn&#8217;t prevent\nyou from at least extracting the cabinet files from <i>.msp<\/i> files.<\/p>\n<p>To enumerate and thereby extract all sub-storages and streams use the OLE\nstructured storage APIs like the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/stgopenstorageex.asp\"> <code>StgOpenStreamEx<\/code> function<\/a> to get a pointer to\nthe <a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/istorage.asp\"> <code>IStorage<\/code> interface<\/a>. Call the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/istorage_enumelements.asp\"> <code>IStorage::EnumElements<\/code> function<\/a> on the interface to get\nthe <a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/ienumstatstg.asp\"> <code>IEnumSTATSTG<\/code> interface<\/a> pointer. As typical with <code>IEnumXXXX<\/code> interface\nimplementations, call the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/ienumstatstg_next.asphttp:\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/ienumstatstg_next.asp\"> <code>Next<\/code> function<\/a>. In this case you get an\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/statstg.asp\"> <code>STATSTG<\/code> structure<\/a>. If the\n<code>STATSTG.type<\/code> field is <code>STGTY_STORAGE<\/code> (1) you&#8217;ve found a transform and the\n<code>STATSTG.pwszName<\/code> is the name of the transform. If the <code>STATSTG.type<\/code> field is\n<code>STGTY_STREAM<\/code> (2) you&#8217;ve found a stream. To determine if the stream is a cabinet\nyou can check the first 4 bytes of the stream for &#8220;MSCF&#8221;.<\/p>\n<p>Patches produced with <i>\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/patchwiz_dll.asp\">PatchWiz.dll<\/a><\/i> from the Windows Installer SDK will\ncontain one cabinet with all files for all transforms in the patch. The files in\nthe cabinet all use the value of the File column of the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/file_table.asp\">File table<\/a> so with a\nquick lookup you can get whatever files you want. This allows you get all of the\nfiles for a patch regardless of what product <i>.msi<\/i> packages the patch\ntargets. Obviously there&#8217;s quite a bit of work here.<\/p>\n<p>A similar approach is to open the <i>.msp<\/i> file using the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/msiopendatabase.asp\"> <code>MsiOpenDatabase<\/code> function<\/a>, passing\n<code>MSIDBOPEN_PATCHFILE<\/code> for the second parameter. Note that this cannot be done in a\ncustom action because the second parameter will marshal as a string so any value\nbesides <code>MSIDBOPEN_READONLY<\/code> (0) won&#8217;t marshal correctly.<\/p>\n<p> You can then use the view APIs like the <code>\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/msidatabaseopenview.asp\">MsiDatabaseOpenView<\/a><\/code>,\n<code>\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/msiviewexecute.asp\">MsiViewExecute<\/a><\/code>, and <code>\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/msiviewfetch.asp\">MsiViewFetch<\/a> <\/code>functions to query the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/_storages_table.asp\">_Storages table<\/a> to get\nthe transforms and the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/_streams_table.asp\">_Streams table<\/a> to get the cabinet file in a patch.\nQuerying the _Streams table in a <i>.msi<\/i> file or a <i>.msm<\/i> file may also return other\nstreams like binaries in the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/binary_table.asp\">Binary table<\/a> or icons in the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/icon_table.asp\">Icon table<\/a>. While you\ncan read data directly from the Data column of the _Streams table using the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/msirecordreadstream.asp\">\n<code>MsiRecordReadStream<\/code> function<\/a> you cannot read from the Data column of the _Storages\ntable. You can use the names and the OLE structured storage APIs as described\nabove to get the exact name of the sub-storage to extract using the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/stg\/stg\/istorage_openstorage.asp\">\n<code>IStorage::OpenStorage<\/code> function<\/a>.\n<\/p>\n<p>There is a much simpler way to accomplish all of this but you&#8217;ll only extract\nfiles from a patch that apply to a specific product since the first pair of\ntransforms to apply to a product from a patch are used. If your patch only\ntargets a single product then you have no worries. You first perform an\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/administrative_installation.asp\">administrative installation<\/a> of the target product\n<i>.msi<\/i> package, which\nruns only basic actions like\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/installfiles_action.asp\">\nInstallFiles<\/a> in the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/adminexecutesequence_table.asp\">\nAdminExecuteSequence table<\/a>. Passing the command to <font face=\"monospace\">\nstart \/wait<\/font>\n<a href=\"\/heaths\/archive\/2005\/11\/15\/493236.aspx\">will block<\/a>\nuntil <i>msiexec.exe<\/i> completes and returns.<\/p>\n<p><font face=\"monospace\">start \/wait msiexec \/a product.msi TARGETDIR=&#8221;%TMP%Product&#8221; \/qn<\/font><\/p>\n<p>Next you apply the patch that contains the files you want to extract. This is\nthe same method you would use to apply any\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/minor_upgrades.asp\">\nminor upgrades<\/a> that a patch might target. Patches will typically transform\nthe AdminExecuteSequence table to add the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/patchfiles_action.asp\">\nPatchFiles<\/a> action.<\/p>\n<p><font face=\"monospace\">start \/wait msiexec \/p patch.msp \/a &#8220;%TMP%Productproduct.msi&#8221; \/qn<\/font><\/p>\n<p>Now the files that were patched in the product will exist in the directory\nstructure and you can fish them out as necessary. If your patch targets multiple products you&#8217;ll need to repeat this\nfor each product, which is why in such cases the more complicated method of file\nextraction described above is beneficial. Note also that any directories that\ndepend upon 64-bit redirection but whose source directories structures are the\nsame will overwrite files because such redirection is not performed for\nadministrative installations. This\n<a href=\"\/heaths\/archive\/2005\/06\/07\/426502.aspx\">happened<\/a>\nwith an early pre-release of the .NET Framework 2.0.<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>From the mailbag, someone asked how to extract files from a patch. Now presumably one would want to extract the files as they apply to a product if the patch were installed but I will cover both ways because one can lead to the other. If you&#8217;re looking for the simplest and quickest way to [&hellip;]<\/p>\n","protected":false},"author":389,"featured_media":3843,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[14,20],"class_list":["post-2443","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-development","tag-installation"],"acf":[],"blog_post_summary":"<p>From the mailbag, someone asked how to extract files from a patch. Now presumably one would want to extract the files as they apply to a product if the patch were installed but I will cover both ways because one can lead to the other. If you&#8217;re looking for the simplest and quickest way to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/posts\/2443","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/users\/389"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/comments?post=2443"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/posts\/2443\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/media\/3843"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/media?parent=2443"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/categories?post=2443"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/tags?post=2443"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}